feat(mesh): mesh_transport.v1 proto contract
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
using ZB.MOM.WW.OtOpcUa.Commons.Protos.MeshTransport.V1;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Commons.Protos;
|
||||
|
||||
/// <summary>
|
||||
/// The single source of truth for which <see cref="CommandEnvelope.CommandOneofCase"/> variants
|
||||
/// the cross-mesh command transport actually handles. Both the contract-lock test (which proves
|
||||
/// this list stays in lock-step with the generated oneof) and the oneof↔domain mapper reference
|
||||
/// this array, so adding a fifth command to <c>mesh_transport.proto</c> forces a matching entry
|
||||
/// here or the build/test goes red.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Mirrors <see cref="TelemetryProtoContract"/>, and for a sharper reason: a telemetry variant
|
||||
/// nobody handles loses an observation, whereas a command variant nobody handles loses a
|
||||
/// deployment, a driver restart or an alarm acknowledgement — silently, since the sender's ack
|
||||
/// only ever meant "received".
|
||||
/// </remarks>
|
||||
public static class MeshTransportProtoContract
|
||||
{
|
||||
/// <summary>
|
||||
/// Exactly the four cross-mesh command oneof cases mirrored from the domain records:
|
||||
/// DispatchDeployment / RestartDriver / ReconnectDriver / AlarmCommand. The fifth message on
|
||||
/// this contract, <c>ApplyAckMessage</c>, travels the opposite direction on its own RPC and is
|
||||
/// not a oneof member, so it is deliberately absent from this list.
|
||||
/// </summary>
|
||||
public static readonly CommandEnvelope.CommandOneofCase[] HandledCases =
|
||||
[
|
||||
CommandEnvelope.CommandOneofCase.DispatchDeployment,
|
||||
CommandEnvelope.CommandOneofCase.RestartDriver,
|
||||
CommandEnvelope.CommandOneofCase.ReconnectDriver,
|
||||
CommandEnvelope.CommandOneofCase.AlarmCommand,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package mesh_transport.v1;
|
||||
|
||||
option csharp_namespace = "ZB.MOM.WW.OtOpcUa.Commons.Protos.MeshTransport.V1";
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
// ClusterClient→gRPC migration Phase 0: the cross-mesh command plane. The five messages that cross
|
||||
// an application-Cluster boundary today ride an Akka ClusterClient receptionist; this contract is
|
||||
// the replacement carrier. Four are central→node commands, one is the node→central deployment ack.
|
||||
//
|
||||
// The C# namespace deliberately says "MeshTransport", never "MeshCommand" — the latter is already
|
||||
// the CLR envelope record ZB.MOM.WW.OtOpcUa.Commons.Messages.Mesh.MeshCommand, and a generated type
|
||||
// of the same name would collide on every file that touches both sides of the mapper.
|
||||
//
|
||||
// Additive-only field evolution: never renumber or reuse a tag; a pre-field peer must read a new
|
||||
// field's proto3 default correctly (which is why nullable domain members are modelled with `optional`
|
||||
// for explicit presence, and why every enum reserves 0 as UNSPECIFIED). The oneof is locked by
|
||||
// MeshTransportProtoContract + its test in the Commons test project.
|
||||
|
||||
// Central → node. Delivered to the node's NodeCommunicationActor, which re-emits the decoded message
|
||||
// on the node's EventStream. ONE-WAY semantics, preserved verbatim from the ClusterClient transport:
|
||||
// the ack means "received and re-emitted", nothing more — not applied, not acknowledged by any
|
||||
// subscriber. No retry is layered on a failure; a failed Execute is logged per node and dropped,
|
||||
// which is an honest upgrade over the receptionist's silent drop rather than a semantic change.
|
||||
service NodeCommandService {
|
||||
rpc Execute (CommandEnvelope) returns (ExecuteAck);
|
||||
}
|
||||
|
||||
// Node → central. Delivered to central's CentralCommunicationActor, which forwards to the
|
||||
// ConfigPublishCoordinator singleton proxy — so an ack landing on either node of the central pair
|
||||
// routes identically, exactly as the receptionist made arrival-node-agnostic. ONE-WAY semantics: the
|
||||
// ack means "handed to the coordinator", not "the deployment is complete".
|
||||
service CentralCommandService {
|
||||
rpc ReportApply (ApplyAckMessage) returns (ReportAck);
|
||||
}
|
||||
|
||||
message CommandEnvelope {
|
||||
// Transport-level correlation, carried for every command including AlarmCommandMessage, which is
|
||||
// the one CLR record with no correlation member of its own. The other three ALSO carry their own
|
||||
// domain correlation field-for-field below; the duplication is deliberate, so each command stays
|
||||
// independently round-trippable through the mapper and the envelope stays loggable on its own.
|
||||
string correlation_id = 1;
|
||||
oneof command {
|
||||
DispatchDeploymentCommand dispatch_deployment = 2; // <- Messages.Deploy.DispatchDeployment
|
||||
RestartDriverCommand restart_driver = 3; // <- Messages.Admin.RestartDriver
|
||||
ReconnectDriverCommand reconnect_driver = 4; // <- Messages.Admin.ReconnectDriver
|
||||
AlarmCommandMessage alarm_command = 5; // <- OpcUa.AlarmCommand
|
||||
}
|
||||
}
|
||||
|
||||
// Deliberately empty. A non-OK gRPC status is the ONLY failure signal on this call — putting a
|
||||
// success flag in the body would invite a caller to read OK+false as a distinct outcome the
|
||||
// fire-and-forget contract does not have. Extensible later without a version bump.
|
||||
message ExecuteAck {
|
||||
}
|
||||
|
||||
// Deliberately empty, for the same reason as ExecuteAck.
|
||||
message ReportAck {
|
||||
}
|
||||
|
||||
// Mirrors ZB.MOM.WW.OtOpcUa.Commons.Messages.Deploy.DispatchDeployment.
|
||||
message DispatchDeploymentCommand {
|
||||
string deployment_id = 1; // DeploymentId (Guid) in its "N" hex form
|
||||
string revision_hash = 2; // RevisionHash — lowercase 64-char SHA-256 hex
|
||||
string correlation_id = 3; // CorrelationId (Guid) in its "N" hex form
|
||||
}
|
||||
|
||||
// Mirrors ZB.MOM.WW.OtOpcUa.Commons.Messages.Admin.RestartDriver.
|
||||
message RestartDriverCommand {
|
||||
string cluster_id = 1;
|
||||
string driver_instance_id = 2;
|
||||
string actor_by_user_name = 3;
|
||||
string correlation_id = 4; // Guid in the record; "N" hex form
|
||||
}
|
||||
|
||||
// Mirrors ZB.MOM.WW.OtOpcUa.Commons.Messages.Admin.ReconnectDriver.
|
||||
message ReconnectDriverCommand {
|
||||
string cluster_id = 1;
|
||||
string driver_instance_id = 2;
|
||||
string actor_by_user_name = 3;
|
||||
string correlation_id = 4; // Guid in the record; "N" hex form
|
||||
}
|
||||
|
||||
// Mirrors ZB.MOM.WW.OtOpcUa.Commons.OpcUa.AlarmCommand.
|
||||
message AlarmCommandMessage {
|
||||
string alarm_id = 1;
|
||||
// The Part 9 operation (Acknowledge / Confirm / OneShotShelve / TimedShelve / Unshelve / Enable /
|
||||
// Disable / AddComment). A `string` in the CLR record, and kept as one here — the same call the
|
||||
// telemetry contract makes for transition_kind / state / level. Promoting it to a proto enum would
|
||||
// put a lossy translation in the mapper for a set the CLR side does not constrain: an operation the
|
||||
// enum did not know would decode to UNSPECIFIED and the command would be dropped, which is the
|
||||
// exact silent-drop class this migration exists to remove.
|
||||
string operation = 2;
|
||||
string user = 3;
|
||||
optional string comment = 4; // nullable in the record — presence distinguishes null from ""
|
||||
google.protobuf.Timestamp unshelve_at_utc = 5; // DateTime? — absent Timestamp encodes null
|
||||
}
|
||||
|
||||
// Mirrors ZB.MOM.WW.OtOpcUa.Commons.Messages.Deploy.ApplyAck.
|
||||
message ApplyAckMessage {
|
||||
string deployment_id = 1; // DeploymentId (Guid) in its "N" hex form
|
||||
string node_id = 2; // NodeId — a logical host name, already a string
|
||||
ApplyOutcome outcome = 3;
|
||||
optional string failure_reason = 4; // nullable in the record — presence distinguishes null from ""
|
||||
string correlation_id = 5; // CorrelationId (Guid) in its "N" hex form
|
||||
}
|
||||
|
||||
// Mirrors ZB.MOM.WW.OtOpcUa.Commons.Messages.Deploy.ApplyAckOutcome, shifted by one so 0 stays
|
||||
// meaningless. proto3 decodes an absent enum field as 0; if 0 were Applied, an ack that lost the
|
||||
// field would report a deployment applied when the node said nothing at all — and the deploy path
|
||||
// seals on exactly that signal.
|
||||
enum ApplyOutcome {
|
||||
APPLY_OUTCOME_UNSPECIFIED = 0;
|
||||
APPLY_OUTCOME_APPLIED = 1;
|
||||
APPLY_OUTCOME_FAILED = 2;
|
||||
}
|
||||
@@ -28,6 +28,7 @@
|
||||
<ItemGroup>
|
||||
<Protobuf Include="Protos\deployment_artifact.proto" GrpcServices="Both"/>
|
||||
<Protobuf Include="Protos\telemetry.proto" GrpcServices="Both"/>
|
||||
<Protobuf Include="Protos\mesh_transport.proto" GrpcServices="Both"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
Reference in New Issue
Block a user