diff --git a/src/ScadaLink.Communication/Protos/sitestream.proto b/src/ScadaLink.Communication/Protos/sitestream.proto
index d01852f..43ffbe3 100644
--- a/src/ScadaLink.Communication/Protos/sitestream.proto
+++ b/src/ScadaLink.Communication/Protos/sitestream.proto
@@ -8,6 +8,7 @@ import "google/protobuf/wrappers.proto"; // Int32Value
service SiteStreamService {
rpc SubscribeInstance(InstanceStreamRequest) returns (stream SiteStreamEvent);
rpc IngestAuditEvents(AuditEventBatch) returns (IngestAck);
+ rpc IngestCachedTelemetry(CachedTelemetryBatch) returns (IngestAck);
}
message InstanceStreamRequest {
@@ -93,3 +94,28 @@ message AuditEventDto {
message AuditEventBatch { repeated AuditEventDto events = 1; }
message IngestAck { repeated string accepted_event_ids = 1; }
+
+// Audit Log (#23) M3 cached-call combined telemetry: a single packet carries
+// both the AuditEvent row to insert and the SiteCalls operational-state upsert
+// for one lifecycle event of a cached outbound call. Central writes both rows
+// in one MS SQL transaction so the audit and operational mirrors never drift.
+message SiteCallOperationalDto {
+ string tracked_operation_id = 1; // GUID string ("D" format)
+ string channel = 2; // "ApiOutbound" | "DbOutbound"
+ string target = 3;
+ string source_site = 4;
+ string status = 5; // AuditStatus name
+ int32 retry_count = 6;
+ string last_error = 7; // empty when null
+ google.protobuf.Int32Value http_status = 8;
+ google.protobuf.Timestamp created_at_utc = 9;
+ google.protobuf.Timestamp updated_at_utc = 10;
+ google.protobuf.Timestamp terminal_at_utc = 11; // absent when not terminal
+}
+
+message CachedTelemetryPacket {
+ AuditEventDto audit_event = 1;
+ SiteCallOperationalDto operational = 2;
+}
+
+message CachedTelemetryBatch { repeated CachedTelemetryPacket packets = 1; }
diff --git a/src/ScadaLink.Communication/SiteStreamGrpc/Sitestream.cs b/src/ScadaLink.Communication/SiteStreamGrpc/Sitestream.cs
index 3a843eb..9639242 100644
--- a/src/ScadaLink.Communication/SiteStreamGrpc/Sitestream.cs
+++ b/src/ScadaLink.Communication/SiteStreamGrpc/Sitestream.cs
@@ -55,19 +55,34 @@ namespace ScadaLink.Communication.Grpc {
"cGF5bG9hZF90cnVuY2F0ZWQYEiABKAgSDQoFZXh0cmEYEyABKAkiPAoPQXVk",
"aXRFdmVudEJhdGNoEikKBmV2ZW50cxgBIAMoCzIZLnNpdGVzdHJlYW0uQXVk",
"aXRFdmVudER0byInCglJbmdlc3RBY2sSGgoSYWNjZXB0ZWRfZXZlbnRfaWRz",
- "GAEgAygJKlwKB1F1YWxpdHkSFwoTUVVBTElUWV9VTlNQRUNJRklFRBAAEhAK",
- "DFFVQUxJVFlfR09PRBABEhUKEVFVQUxJVFlfVU5DRVJUQUlOEAISDwoLUVVB",
- "TElUWV9CQUQQAypdCg5BbGFybVN0YXRlRW51bRIbChdBTEFSTV9TVEFURV9V",
- "TlNQRUNJRklFRBAAEhYKEkFMQVJNX1NUQVRFX05PUk1BTBABEhYKEkFMQVJN",
- "X1NUQVRFX0FDVElWRRACKoUBCg5BbGFybUxldmVsRW51bRIUChBBTEFSTV9M",
- "RVZFTF9OT05FEAASEwoPQUxBUk1fTEVWRUxfTE9XEAESFwoTQUxBUk1fTEVW",
- "RUxfTE9XX0xPVxACEhQKEEFMQVJNX0xFVkVMX0hJR0gQAxIZChVBTEFSTV9M",
- "RVZFTF9ISUdIX0hJR0gQBDKzAQoRU2l0ZVN0cmVhbVNlcnZpY2USVQoRU3Vi",
- "c2NyaWJlSW5zdGFuY2USIS5zaXRlc3RyZWFtLkluc3RhbmNlU3RyZWFtUmVx",
- "dWVzdBobLnNpdGVzdHJlYW0uU2l0ZVN0cmVhbUV2ZW50MAESRwoRSW5nZXN0",
- "QXVkaXRFdmVudHMSGy5zaXRlc3RyZWFtLkF1ZGl0RXZlbnRCYXRjaBoVLnNp",
- "dGVzdHJlYW0uSW5nZXN0QWNrQh+qAhxTY2FkYUxpbmsuQ29tbXVuaWNhdGlv",
- "bi5HcnBjYgZwcm90bzM="));
+ "GAEgAygJIvQCChZTaXRlQ2FsbE9wZXJhdGlvbmFsRHRvEhwKFHRyYWNrZWRf",
+ "b3BlcmF0aW9uX2lkGAEgASgJEg8KB2NoYW5uZWwYAiABKAkSDgoGdGFyZ2V0",
+ "GAMgASgJEhMKC3NvdXJjZV9zaXRlGAQgASgJEg4KBnN0YXR1cxgFIAEoCRIT",
+ "CgtyZXRyeV9jb3VudBgGIAEoBRISCgpsYXN0X2Vycm9yGAcgASgJEjAKC2h0",
+ "dHBfc3RhdHVzGAggASgLMhsuZ29vZ2xlLnByb3RvYnVmLkludDMyVmFsdWUS",
+ "MgoOY3JlYXRlZF9hdF91dGMYCSABKAsyGi5nb29nbGUucHJvdG9idWYuVGlt",
+ "ZXN0YW1wEjIKDnVwZGF0ZWRfYXRfdXRjGAogASgLMhouZ29vZ2xlLnByb3Rv",
+ "YnVmLlRpbWVzdGFtcBIzCg90ZXJtaW5hbF9hdF91dGMYCyABKAsyGi5nb29n",
+ "bGUucHJvdG9idWYuVGltZXN0YW1wIoABChVDYWNoZWRUZWxlbWV0cnlQYWNr",
+ "ZXQSLgoLYXVkaXRfZXZlbnQYASABKAsyGS5zaXRlc3RyZWFtLkF1ZGl0RXZl",
+ "bnREdG8SNwoLb3BlcmF0aW9uYWwYAiABKAsyIi5zaXRlc3RyZWFtLlNpdGVD",
+ "YWxsT3BlcmF0aW9uYWxEdG8iSgoUQ2FjaGVkVGVsZW1ldHJ5QmF0Y2gSMgoH",
+ "cGFja2V0cxgBIAMoCzIhLnNpdGVzdHJlYW0uQ2FjaGVkVGVsZW1ldHJ5UGFj",
+ "a2V0KlwKB1F1YWxpdHkSFwoTUVVBTElUWV9VTlNQRUNJRklFRBAAEhAKDFFV",
+ "QUxJVFlfR09PRBABEhUKEVFVQUxJVFlfVU5DRVJUQUlOEAISDwoLUVVBTElU",
+ "WV9CQUQQAypdCg5BbGFybVN0YXRlRW51bRIbChdBTEFSTV9TVEFURV9VTlNQ",
+ "RUNJRklFRBAAEhYKEkFMQVJNX1NUQVRFX05PUk1BTBABEhYKEkFMQVJNX1NU",
+ "QVRFX0FDVElWRRACKoUBCg5BbGFybUxldmVsRW51bRIUChBBTEFSTV9MRVZF",
+ "TF9OT05FEAASEwoPQUxBUk1fTEVWRUxfTE9XEAESFwoTQUxBUk1fTEVWRUxf",
+ "TE9XX0xPVxACEhQKEEFMQVJNX0xFVkVMX0hJR0gQAxIZChVBTEFSTV9MRVZF",
+ "TF9ISUdIX0hJR0gQBDKFAgoRU2l0ZVN0cmVhbVNlcnZpY2USVQoRU3Vic2Ny",
+ "aWJlSW5zdGFuY2USIS5zaXRlc3RyZWFtLkluc3RhbmNlU3RyZWFtUmVxdWVz",
+ "dBobLnNpdGVzdHJlYW0uU2l0ZVN0cmVhbUV2ZW50MAESRwoRSW5nZXN0QXVk",
+ "aXRFdmVudHMSGy5zaXRlc3RyZWFtLkF1ZGl0RXZlbnRCYXRjaBoVLnNpdGVz",
+ "dHJlYW0uSW5nZXN0QWNrElAKFUluZ2VzdENhY2hlZFRlbGVtZXRyeRIgLnNp",
+ "dGVzdHJlYW0uQ2FjaGVkVGVsZW1ldHJ5QmF0Y2gaFS5zaXRlc3RyZWFtLklu",
+ "Z2VzdEFja0IfqgIcU2NhZGFMaW5rLkNvbW11bmljYXRpb24uR3JwY2IGcHJv",
+ "dG8z"));
descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { global::Google.Protobuf.WellKnownTypes.TimestampReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.WrappersReflection.Descriptor, },
new pbr::GeneratedClrTypeInfo(new[] {typeof(global::ScadaLink.Communication.Grpc.Quality), typeof(global::ScadaLink.Communication.Grpc.AlarmStateEnum), typeof(global::ScadaLink.Communication.Grpc.AlarmLevelEnum), }, null, new pbr::GeneratedClrTypeInfo[] {
@@ -77,7 +92,10 @@ namespace ScadaLink.Communication.Grpc {
new pbr::GeneratedClrTypeInfo(typeof(global::ScadaLink.Communication.Grpc.AlarmStateUpdate), global::ScadaLink.Communication.Grpc.AlarmStateUpdate.Parser, new[]{ "InstanceUniqueName", "AlarmName", "State", "Priority", "Timestamp", "Level", "Message" }, null, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::ScadaLink.Communication.Grpc.AuditEventDto), global::ScadaLink.Communication.Grpc.AuditEventDto.Parser, new[]{ "EventId", "OccurredAtUtc", "Channel", "Kind", "CorrelationId", "SourceSiteId", "SourceInstanceId", "SourceScript", "Actor", "Target", "Status", "HttpStatus", "DurationMs", "ErrorMessage", "ErrorDetail", "RequestSummary", "ResponseSummary", "PayloadTruncated", "Extra" }, null, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::ScadaLink.Communication.Grpc.AuditEventBatch), global::ScadaLink.Communication.Grpc.AuditEventBatch.Parser, new[]{ "Events" }, null, null, null, null),
- new pbr::GeneratedClrTypeInfo(typeof(global::ScadaLink.Communication.Grpc.IngestAck), global::ScadaLink.Communication.Grpc.IngestAck.Parser, new[]{ "AcceptedEventIds" }, null, null, null, null)
+ new pbr::GeneratedClrTypeInfo(typeof(global::ScadaLink.Communication.Grpc.IngestAck), global::ScadaLink.Communication.Grpc.IngestAck.Parser, new[]{ "AcceptedEventIds" }, null, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::ScadaLink.Communication.Grpc.SiteCallOperationalDto), global::ScadaLink.Communication.Grpc.SiteCallOperationalDto.Parser, new[]{ "TrackedOperationId", "Channel", "Target", "SourceSite", "Status", "RetryCount", "LastError", "HttpStatus", "CreatedAtUtc", "UpdatedAtUtc", "TerminalAtUtc" }, null, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::ScadaLink.Communication.Grpc.CachedTelemetryPacket), global::ScadaLink.Communication.Grpc.CachedTelemetryPacket.Parser, new[]{ "AuditEvent", "Operational" }, null, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::ScadaLink.Communication.Grpc.CachedTelemetryBatch), global::ScadaLink.Communication.Grpc.CachedTelemetryBatch.Parser, new[]{ "Packets" }, null, null, null, null)
}));
}
#endregion
@@ -2780,6 +2798,1070 @@ namespace ScadaLink.Communication.Grpc {
}
+ ///
+ /// Audit Log (#23) M3 cached-call combined telemetry: a single packet carries
+ /// both the AuditEvent row to insert and the SiteCalls operational-state upsert
+ /// for one lifecycle event of a cached outbound call. Central writes both rows
+ /// in one MS SQL transaction so the audit and operational mirrors never drift.
+ ///
+ [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")]
+ public sealed partial class SiteCallOperationalDto : pb::IMessage
+ #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+ , pb::IBufferMessage
+ #endif
+ {
+ private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new SiteCallOperationalDto());
+ private pb::UnknownFieldSet _unknownFields;
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public static pb::MessageParser Parser { get { return _parser; } }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public static pbr::MessageDescriptor Descriptor {
+ get { return global::ScadaLink.Communication.Grpc.SitestreamReflection.Descriptor.MessageTypes[7]; }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ pbr::MessageDescriptor pb::IMessage.Descriptor {
+ get { return Descriptor; }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public SiteCallOperationalDto() {
+ OnConstruction();
+ }
+
+ partial void OnConstruction();
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public SiteCallOperationalDto(SiteCallOperationalDto other) : this() {
+ trackedOperationId_ = other.trackedOperationId_;
+ channel_ = other.channel_;
+ target_ = other.target_;
+ sourceSite_ = other.sourceSite_;
+ status_ = other.status_;
+ retryCount_ = other.retryCount_;
+ lastError_ = other.lastError_;
+ HttpStatus = other.HttpStatus;
+ createdAtUtc_ = other.createdAtUtc_ != null ? other.createdAtUtc_.Clone() : null;
+ updatedAtUtc_ = other.updatedAtUtc_ != null ? other.updatedAtUtc_.Clone() : null;
+ terminalAtUtc_ = other.terminalAtUtc_ != null ? other.terminalAtUtc_.Clone() : null;
+ _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public SiteCallOperationalDto Clone() {
+ return new SiteCallOperationalDto(this);
+ }
+
+ /// Field number for the "tracked_operation_id" field.
+ public const int TrackedOperationIdFieldNumber = 1;
+ private string trackedOperationId_ = "";
+ ///
+ /// GUID string ("D" format)
+ ///
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public string TrackedOperationId {
+ get { return trackedOperationId_; }
+ set {
+ trackedOperationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+ }
+ }
+
+ /// Field number for the "channel" field.
+ public const int ChannelFieldNumber = 2;
+ private string channel_ = "";
+ ///
+ /// "ApiOutbound" | "DbOutbound"
+ ///
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public string Channel {
+ get { return channel_; }
+ set {
+ channel_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+ }
+ }
+
+ /// Field number for the "target" field.
+ public const int TargetFieldNumber = 3;
+ private string target_ = "";
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public string Target {
+ get { return target_; }
+ set {
+ target_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+ }
+ }
+
+ /// Field number for the "source_site" field.
+ public const int SourceSiteFieldNumber = 4;
+ private string sourceSite_ = "";
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public string SourceSite {
+ get { return sourceSite_; }
+ set {
+ sourceSite_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+ }
+ }
+
+ /// Field number for the "status" field.
+ public const int StatusFieldNumber = 5;
+ private string status_ = "";
+ ///
+ /// AuditStatus name
+ ///
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public string Status {
+ get { return status_; }
+ set {
+ status_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+ }
+ }
+
+ /// Field number for the "retry_count" field.
+ public const int RetryCountFieldNumber = 6;
+ private int retryCount_;
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public int RetryCount {
+ get { return retryCount_; }
+ set {
+ retryCount_ = value;
+ }
+ }
+
+ /// Field number for the "last_error" field.
+ public const int LastErrorFieldNumber = 7;
+ private string lastError_ = "";
+ ///
+ /// empty when null
+ ///
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public string LastError {
+ get { return lastError_; }
+ set {
+ lastError_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+ }
+ }
+
+ /// Field number for the "http_status" field.
+ public const int HttpStatusFieldNumber = 8;
+ private static readonly pb::FieldCodec _single_httpStatus_codec = pb::FieldCodec.ForStructWrapper(66);
+ private int? httpStatus_;
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public int? HttpStatus {
+ get { return httpStatus_; }
+ set {
+ httpStatus_ = value;
+ }
+ }
+
+
+ /// Field number for the "created_at_utc" field.
+ public const int CreatedAtUtcFieldNumber = 9;
+ private global::Google.Protobuf.WellKnownTypes.Timestamp createdAtUtc_;
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public global::Google.Protobuf.WellKnownTypes.Timestamp CreatedAtUtc {
+ get { return createdAtUtc_; }
+ set {
+ createdAtUtc_ = value;
+ }
+ }
+
+ /// Field number for the "updated_at_utc" field.
+ public const int UpdatedAtUtcFieldNumber = 10;
+ private global::Google.Protobuf.WellKnownTypes.Timestamp updatedAtUtc_;
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public global::Google.Protobuf.WellKnownTypes.Timestamp UpdatedAtUtc {
+ get { return updatedAtUtc_; }
+ set {
+ updatedAtUtc_ = value;
+ }
+ }
+
+ /// Field number for the "terminal_at_utc" field.
+ public const int TerminalAtUtcFieldNumber = 11;
+ private global::Google.Protobuf.WellKnownTypes.Timestamp terminalAtUtc_;
+ ///
+ /// absent when not terminal
+ ///
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public global::Google.Protobuf.WellKnownTypes.Timestamp TerminalAtUtc {
+ get { return terminalAtUtc_; }
+ set {
+ terminalAtUtc_ = value;
+ }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public override bool Equals(object other) {
+ return Equals(other as SiteCallOperationalDto);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public bool Equals(SiteCallOperationalDto other) {
+ if (ReferenceEquals(other, null)) {
+ return false;
+ }
+ if (ReferenceEquals(other, this)) {
+ return true;
+ }
+ if (TrackedOperationId != other.TrackedOperationId) return false;
+ if (Channel != other.Channel) return false;
+ if (Target != other.Target) return false;
+ if (SourceSite != other.SourceSite) return false;
+ if (Status != other.Status) return false;
+ if (RetryCount != other.RetryCount) return false;
+ if (LastError != other.LastError) return false;
+ if (HttpStatus != other.HttpStatus) return false;
+ if (!object.Equals(CreatedAtUtc, other.CreatedAtUtc)) return false;
+ if (!object.Equals(UpdatedAtUtc, other.UpdatedAtUtc)) return false;
+ if (!object.Equals(TerminalAtUtc, other.TerminalAtUtc)) return false;
+ return Equals(_unknownFields, other._unknownFields);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public override int GetHashCode() {
+ int hash = 1;
+ if (TrackedOperationId.Length != 0) hash ^= TrackedOperationId.GetHashCode();
+ if (Channel.Length != 0) hash ^= Channel.GetHashCode();
+ if (Target.Length != 0) hash ^= Target.GetHashCode();
+ if (SourceSite.Length != 0) hash ^= SourceSite.GetHashCode();
+ if (Status.Length != 0) hash ^= Status.GetHashCode();
+ if (RetryCount != 0) hash ^= RetryCount.GetHashCode();
+ if (LastError.Length != 0) hash ^= LastError.GetHashCode();
+ if (httpStatus_ != null) hash ^= HttpStatus.GetHashCode();
+ if (createdAtUtc_ != null) hash ^= CreatedAtUtc.GetHashCode();
+ if (updatedAtUtc_ != null) hash ^= UpdatedAtUtc.GetHashCode();
+ if (terminalAtUtc_ != null) hash ^= TerminalAtUtc.GetHashCode();
+ if (_unknownFields != null) {
+ hash ^= _unknownFields.GetHashCode();
+ }
+ return hash;
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public override string ToString() {
+ return pb::JsonFormatter.ToDiagnosticString(this);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public void WriteTo(pb::CodedOutputStream output) {
+ #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+ output.WriteRawMessage(this);
+ #else
+ if (TrackedOperationId.Length != 0) {
+ output.WriteRawTag(10);
+ output.WriteString(TrackedOperationId);
+ }
+ if (Channel.Length != 0) {
+ output.WriteRawTag(18);
+ output.WriteString(Channel);
+ }
+ if (Target.Length != 0) {
+ output.WriteRawTag(26);
+ output.WriteString(Target);
+ }
+ if (SourceSite.Length != 0) {
+ output.WriteRawTag(34);
+ output.WriteString(SourceSite);
+ }
+ if (Status.Length != 0) {
+ output.WriteRawTag(42);
+ output.WriteString(Status);
+ }
+ if (RetryCount != 0) {
+ output.WriteRawTag(48);
+ output.WriteInt32(RetryCount);
+ }
+ if (LastError.Length != 0) {
+ output.WriteRawTag(58);
+ output.WriteString(LastError);
+ }
+ if (httpStatus_ != null) {
+ _single_httpStatus_codec.WriteTagAndValue(output, HttpStatus);
+ }
+ if (createdAtUtc_ != null) {
+ output.WriteRawTag(74);
+ output.WriteMessage(CreatedAtUtc);
+ }
+ if (updatedAtUtc_ != null) {
+ output.WriteRawTag(82);
+ output.WriteMessage(UpdatedAtUtc);
+ }
+ if (terminalAtUtc_ != null) {
+ output.WriteRawTag(90);
+ output.WriteMessage(TerminalAtUtc);
+ }
+ if (_unknownFields != null) {
+ _unknownFields.WriteTo(output);
+ }
+ #endif
+ }
+
+ #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+ if (TrackedOperationId.Length != 0) {
+ output.WriteRawTag(10);
+ output.WriteString(TrackedOperationId);
+ }
+ if (Channel.Length != 0) {
+ output.WriteRawTag(18);
+ output.WriteString(Channel);
+ }
+ if (Target.Length != 0) {
+ output.WriteRawTag(26);
+ output.WriteString(Target);
+ }
+ if (SourceSite.Length != 0) {
+ output.WriteRawTag(34);
+ output.WriteString(SourceSite);
+ }
+ if (Status.Length != 0) {
+ output.WriteRawTag(42);
+ output.WriteString(Status);
+ }
+ if (RetryCount != 0) {
+ output.WriteRawTag(48);
+ output.WriteInt32(RetryCount);
+ }
+ if (LastError.Length != 0) {
+ output.WriteRawTag(58);
+ output.WriteString(LastError);
+ }
+ if (httpStatus_ != null) {
+ _single_httpStatus_codec.WriteTagAndValue(ref output, HttpStatus);
+ }
+ if (createdAtUtc_ != null) {
+ output.WriteRawTag(74);
+ output.WriteMessage(CreatedAtUtc);
+ }
+ if (updatedAtUtc_ != null) {
+ output.WriteRawTag(82);
+ output.WriteMessage(UpdatedAtUtc);
+ }
+ if (terminalAtUtc_ != null) {
+ output.WriteRawTag(90);
+ output.WriteMessage(TerminalAtUtc);
+ }
+ if (_unknownFields != null) {
+ _unknownFields.WriteTo(ref output);
+ }
+ }
+ #endif
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public int CalculateSize() {
+ int size = 0;
+ if (TrackedOperationId.Length != 0) {
+ size += 1 + pb::CodedOutputStream.ComputeStringSize(TrackedOperationId);
+ }
+ if (Channel.Length != 0) {
+ size += 1 + pb::CodedOutputStream.ComputeStringSize(Channel);
+ }
+ if (Target.Length != 0) {
+ size += 1 + pb::CodedOutputStream.ComputeStringSize(Target);
+ }
+ if (SourceSite.Length != 0) {
+ size += 1 + pb::CodedOutputStream.ComputeStringSize(SourceSite);
+ }
+ if (Status.Length != 0) {
+ size += 1 + pb::CodedOutputStream.ComputeStringSize(Status);
+ }
+ if (RetryCount != 0) {
+ size += 1 + pb::CodedOutputStream.ComputeInt32Size(RetryCount);
+ }
+ if (LastError.Length != 0) {
+ size += 1 + pb::CodedOutputStream.ComputeStringSize(LastError);
+ }
+ if (httpStatus_ != null) {
+ size += _single_httpStatus_codec.CalculateSizeWithTag(HttpStatus);
+ }
+ if (createdAtUtc_ != null) {
+ size += 1 + pb::CodedOutputStream.ComputeMessageSize(CreatedAtUtc);
+ }
+ if (updatedAtUtc_ != null) {
+ size += 1 + pb::CodedOutputStream.ComputeMessageSize(UpdatedAtUtc);
+ }
+ if (terminalAtUtc_ != null) {
+ size += 1 + pb::CodedOutputStream.ComputeMessageSize(TerminalAtUtc);
+ }
+ if (_unknownFields != null) {
+ size += _unknownFields.CalculateSize();
+ }
+ return size;
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public void MergeFrom(SiteCallOperationalDto other) {
+ if (other == null) {
+ return;
+ }
+ if (other.TrackedOperationId.Length != 0) {
+ TrackedOperationId = other.TrackedOperationId;
+ }
+ if (other.Channel.Length != 0) {
+ Channel = other.Channel;
+ }
+ if (other.Target.Length != 0) {
+ Target = other.Target;
+ }
+ if (other.SourceSite.Length != 0) {
+ SourceSite = other.SourceSite;
+ }
+ if (other.Status.Length != 0) {
+ Status = other.Status;
+ }
+ if (other.RetryCount != 0) {
+ RetryCount = other.RetryCount;
+ }
+ if (other.LastError.Length != 0) {
+ LastError = other.LastError;
+ }
+ if (other.httpStatus_ != null) {
+ if (httpStatus_ == null || other.HttpStatus != 0) {
+ HttpStatus = other.HttpStatus;
+ }
+ }
+ if (other.createdAtUtc_ != null) {
+ if (createdAtUtc_ == null) {
+ CreatedAtUtc = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+ }
+ CreatedAtUtc.MergeFrom(other.CreatedAtUtc);
+ }
+ if (other.updatedAtUtc_ != null) {
+ if (updatedAtUtc_ == null) {
+ UpdatedAtUtc = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+ }
+ UpdatedAtUtc.MergeFrom(other.UpdatedAtUtc);
+ }
+ if (other.terminalAtUtc_ != null) {
+ if (terminalAtUtc_ == null) {
+ TerminalAtUtc = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+ }
+ TerminalAtUtc.MergeFrom(other.TerminalAtUtc);
+ }
+ _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public void MergeFrom(pb::CodedInputStream input) {
+ #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+ input.ReadRawMessage(this);
+ #else
+ uint tag;
+ while ((tag = input.ReadTag()) != 0) {
+ if ((tag & 7) == 4) {
+ // Abort on any end group tag.
+ return;
+ }
+ switch(tag) {
+ default:
+ _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+ break;
+ case 10: {
+ TrackedOperationId = input.ReadString();
+ break;
+ }
+ case 18: {
+ Channel = input.ReadString();
+ break;
+ }
+ case 26: {
+ Target = input.ReadString();
+ break;
+ }
+ case 34: {
+ SourceSite = input.ReadString();
+ break;
+ }
+ case 42: {
+ Status = input.ReadString();
+ break;
+ }
+ case 48: {
+ RetryCount = input.ReadInt32();
+ break;
+ }
+ case 58: {
+ LastError = input.ReadString();
+ break;
+ }
+ case 66: {
+ int? value = _single_httpStatus_codec.Read(input);
+ if (httpStatus_ == null || value != 0) {
+ HttpStatus = value;
+ }
+ break;
+ }
+ case 74: {
+ if (createdAtUtc_ == null) {
+ CreatedAtUtc = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+ }
+ input.ReadMessage(CreatedAtUtc);
+ break;
+ }
+ case 82: {
+ if (updatedAtUtc_ == null) {
+ UpdatedAtUtc = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+ }
+ input.ReadMessage(UpdatedAtUtc);
+ break;
+ }
+ case 90: {
+ if (terminalAtUtc_ == null) {
+ TerminalAtUtc = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+ }
+ input.ReadMessage(TerminalAtUtc);
+ break;
+ }
+ }
+ }
+ #endif
+ }
+
+ #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+ uint tag;
+ while ((tag = input.ReadTag()) != 0) {
+ if ((tag & 7) == 4) {
+ // Abort on any end group tag.
+ return;
+ }
+ switch(tag) {
+ default:
+ _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+ break;
+ case 10: {
+ TrackedOperationId = input.ReadString();
+ break;
+ }
+ case 18: {
+ Channel = input.ReadString();
+ break;
+ }
+ case 26: {
+ Target = input.ReadString();
+ break;
+ }
+ case 34: {
+ SourceSite = input.ReadString();
+ break;
+ }
+ case 42: {
+ Status = input.ReadString();
+ break;
+ }
+ case 48: {
+ RetryCount = input.ReadInt32();
+ break;
+ }
+ case 58: {
+ LastError = input.ReadString();
+ break;
+ }
+ case 66: {
+ int? value = _single_httpStatus_codec.Read(ref input);
+ if (httpStatus_ == null || value != 0) {
+ HttpStatus = value;
+ }
+ break;
+ }
+ case 74: {
+ if (createdAtUtc_ == null) {
+ CreatedAtUtc = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+ }
+ input.ReadMessage(CreatedAtUtc);
+ break;
+ }
+ case 82: {
+ if (updatedAtUtc_ == null) {
+ UpdatedAtUtc = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+ }
+ input.ReadMessage(UpdatedAtUtc);
+ break;
+ }
+ case 90: {
+ if (terminalAtUtc_ == null) {
+ TerminalAtUtc = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+ }
+ input.ReadMessage(TerminalAtUtc);
+ break;
+ }
+ }
+ }
+ }
+ #endif
+
+ }
+
+ [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")]
+ public sealed partial class CachedTelemetryPacket : pb::IMessage
+ #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+ , pb::IBufferMessage
+ #endif
+ {
+ private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new CachedTelemetryPacket());
+ private pb::UnknownFieldSet _unknownFields;
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public static pb::MessageParser Parser { get { return _parser; } }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public static pbr::MessageDescriptor Descriptor {
+ get { return global::ScadaLink.Communication.Grpc.SitestreamReflection.Descriptor.MessageTypes[8]; }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ pbr::MessageDescriptor pb::IMessage.Descriptor {
+ get { return Descriptor; }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public CachedTelemetryPacket() {
+ OnConstruction();
+ }
+
+ partial void OnConstruction();
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public CachedTelemetryPacket(CachedTelemetryPacket other) : this() {
+ auditEvent_ = other.auditEvent_ != null ? other.auditEvent_.Clone() : null;
+ operational_ = other.operational_ != null ? other.operational_.Clone() : null;
+ _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public CachedTelemetryPacket Clone() {
+ return new CachedTelemetryPacket(this);
+ }
+
+ /// Field number for the "audit_event" field.
+ public const int AuditEventFieldNumber = 1;
+ private global::ScadaLink.Communication.Grpc.AuditEventDto auditEvent_;
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public global::ScadaLink.Communication.Grpc.AuditEventDto AuditEvent {
+ get { return auditEvent_; }
+ set {
+ auditEvent_ = value;
+ }
+ }
+
+ /// Field number for the "operational" field.
+ public const int OperationalFieldNumber = 2;
+ private global::ScadaLink.Communication.Grpc.SiteCallOperationalDto operational_;
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public global::ScadaLink.Communication.Grpc.SiteCallOperationalDto Operational {
+ get { return operational_; }
+ set {
+ operational_ = value;
+ }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public override bool Equals(object other) {
+ return Equals(other as CachedTelemetryPacket);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public bool Equals(CachedTelemetryPacket other) {
+ if (ReferenceEquals(other, null)) {
+ return false;
+ }
+ if (ReferenceEquals(other, this)) {
+ return true;
+ }
+ if (!object.Equals(AuditEvent, other.AuditEvent)) return false;
+ if (!object.Equals(Operational, other.Operational)) return false;
+ return Equals(_unknownFields, other._unknownFields);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public override int GetHashCode() {
+ int hash = 1;
+ if (auditEvent_ != null) hash ^= AuditEvent.GetHashCode();
+ if (operational_ != null) hash ^= Operational.GetHashCode();
+ if (_unknownFields != null) {
+ hash ^= _unknownFields.GetHashCode();
+ }
+ return hash;
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public override string ToString() {
+ return pb::JsonFormatter.ToDiagnosticString(this);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public void WriteTo(pb::CodedOutputStream output) {
+ #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+ output.WriteRawMessage(this);
+ #else
+ if (auditEvent_ != null) {
+ output.WriteRawTag(10);
+ output.WriteMessage(AuditEvent);
+ }
+ if (operational_ != null) {
+ output.WriteRawTag(18);
+ output.WriteMessage(Operational);
+ }
+ if (_unknownFields != null) {
+ _unknownFields.WriteTo(output);
+ }
+ #endif
+ }
+
+ #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+ if (auditEvent_ != null) {
+ output.WriteRawTag(10);
+ output.WriteMessage(AuditEvent);
+ }
+ if (operational_ != null) {
+ output.WriteRawTag(18);
+ output.WriteMessage(Operational);
+ }
+ if (_unknownFields != null) {
+ _unknownFields.WriteTo(ref output);
+ }
+ }
+ #endif
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public int CalculateSize() {
+ int size = 0;
+ if (auditEvent_ != null) {
+ size += 1 + pb::CodedOutputStream.ComputeMessageSize(AuditEvent);
+ }
+ if (operational_ != null) {
+ size += 1 + pb::CodedOutputStream.ComputeMessageSize(Operational);
+ }
+ if (_unknownFields != null) {
+ size += _unknownFields.CalculateSize();
+ }
+ return size;
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public void MergeFrom(CachedTelemetryPacket other) {
+ if (other == null) {
+ return;
+ }
+ if (other.auditEvent_ != null) {
+ if (auditEvent_ == null) {
+ AuditEvent = new global::ScadaLink.Communication.Grpc.AuditEventDto();
+ }
+ AuditEvent.MergeFrom(other.AuditEvent);
+ }
+ if (other.operational_ != null) {
+ if (operational_ == null) {
+ Operational = new global::ScadaLink.Communication.Grpc.SiteCallOperationalDto();
+ }
+ Operational.MergeFrom(other.Operational);
+ }
+ _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public void MergeFrom(pb::CodedInputStream input) {
+ #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+ input.ReadRawMessage(this);
+ #else
+ uint tag;
+ while ((tag = input.ReadTag()) != 0) {
+ if ((tag & 7) == 4) {
+ // Abort on any end group tag.
+ return;
+ }
+ switch(tag) {
+ default:
+ _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+ break;
+ case 10: {
+ if (auditEvent_ == null) {
+ AuditEvent = new global::ScadaLink.Communication.Grpc.AuditEventDto();
+ }
+ input.ReadMessage(AuditEvent);
+ break;
+ }
+ case 18: {
+ if (operational_ == null) {
+ Operational = new global::ScadaLink.Communication.Grpc.SiteCallOperationalDto();
+ }
+ input.ReadMessage(Operational);
+ break;
+ }
+ }
+ }
+ #endif
+ }
+
+ #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+ uint tag;
+ while ((tag = input.ReadTag()) != 0) {
+ if ((tag & 7) == 4) {
+ // Abort on any end group tag.
+ return;
+ }
+ switch(tag) {
+ default:
+ _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+ break;
+ case 10: {
+ if (auditEvent_ == null) {
+ AuditEvent = new global::ScadaLink.Communication.Grpc.AuditEventDto();
+ }
+ input.ReadMessage(AuditEvent);
+ break;
+ }
+ case 18: {
+ if (operational_ == null) {
+ Operational = new global::ScadaLink.Communication.Grpc.SiteCallOperationalDto();
+ }
+ input.ReadMessage(Operational);
+ break;
+ }
+ }
+ }
+ }
+ #endif
+
+ }
+
+ [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")]
+ public sealed partial class CachedTelemetryBatch : pb::IMessage
+ #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+ , pb::IBufferMessage
+ #endif
+ {
+ private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new CachedTelemetryBatch());
+ private pb::UnknownFieldSet _unknownFields;
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public static pb::MessageParser Parser { get { return _parser; } }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public static pbr::MessageDescriptor Descriptor {
+ get { return global::ScadaLink.Communication.Grpc.SitestreamReflection.Descriptor.MessageTypes[9]; }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ pbr::MessageDescriptor pb::IMessage.Descriptor {
+ get { return Descriptor; }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public CachedTelemetryBatch() {
+ OnConstruction();
+ }
+
+ partial void OnConstruction();
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public CachedTelemetryBatch(CachedTelemetryBatch other) : this() {
+ packets_ = other.packets_.Clone();
+ _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public CachedTelemetryBatch Clone() {
+ return new CachedTelemetryBatch(this);
+ }
+
+ /// Field number for the "packets" field.
+ public const int PacketsFieldNumber = 1;
+ private static readonly pb::FieldCodec _repeated_packets_codec
+ = pb::FieldCodec.ForMessage(10, global::ScadaLink.Communication.Grpc.CachedTelemetryPacket.Parser);
+ private readonly pbc::RepeatedField packets_ = new pbc::RepeatedField();
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public pbc::RepeatedField Packets {
+ get { return packets_; }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public override bool Equals(object other) {
+ return Equals(other as CachedTelemetryBatch);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public bool Equals(CachedTelemetryBatch other) {
+ if (ReferenceEquals(other, null)) {
+ return false;
+ }
+ if (ReferenceEquals(other, this)) {
+ return true;
+ }
+ if(!packets_.Equals(other.packets_)) return false;
+ return Equals(_unknownFields, other._unknownFields);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public override int GetHashCode() {
+ int hash = 1;
+ hash ^= packets_.GetHashCode();
+ if (_unknownFields != null) {
+ hash ^= _unknownFields.GetHashCode();
+ }
+ return hash;
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public override string ToString() {
+ return pb::JsonFormatter.ToDiagnosticString(this);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public void WriteTo(pb::CodedOutputStream output) {
+ #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+ output.WriteRawMessage(this);
+ #else
+ packets_.WriteTo(output, _repeated_packets_codec);
+ if (_unknownFields != null) {
+ _unknownFields.WriteTo(output);
+ }
+ #endif
+ }
+
+ #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+ packets_.WriteTo(ref output, _repeated_packets_codec);
+ if (_unknownFields != null) {
+ _unknownFields.WriteTo(ref output);
+ }
+ }
+ #endif
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public int CalculateSize() {
+ int size = 0;
+ size += packets_.CalculateSize(_repeated_packets_codec);
+ if (_unknownFields != null) {
+ size += _unknownFields.CalculateSize();
+ }
+ return size;
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public void MergeFrom(CachedTelemetryBatch other) {
+ if (other == null) {
+ return;
+ }
+ packets_.Add(other.packets_);
+ _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public void MergeFrom(pb::CodedInputStream input) {
+ #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+ input.ReadRawMessage(this);
+ #else
+ uint tag;
+ while ((tag = input.ReadTag()) != 0) {
+ if ((tag & 7) == 4) {
+ // Abort on any end group tag.
+ return;
+ }
+ switch(tag) {
+ default:
+ _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+ break;
+ case 10: {
+ packets_.AddEntriesFrom(input, _repeated_packets_codec);
+ break;
+ }
+ }
+ }
+ #endif
+ }
+
+ #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+ uint tag;
+ while ((tag = input.ReadTag()) != 0) {
+ if ((tag & 7) == 4) {
+ // Abort on any end group tag.
+ return;
+ }
+ switch(tag) {
+ default:
+ _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+ break;
+ case 10: {
+ packets_.AddEntriesFrom(ref input, _repeated_packets_codec);
+ break;
+ }
+ }
+ }
+ }
+ #endif
+
+ }
+
#endregion
}
diff --git a/src/ScadaLink.Communication/SiteStreamGrpc/SitestreamGrpc.cs b/src/ScadaLink.Communication/SiteStreamGrpc/SitestreamGrpc.cs
index 0a900cb..e7b9b33 100644
--- a/src/ScadaLink.Communication/SiteStreamGrpc/SitestreamGrpc.cs
+++ b/src/ScadaLink.Communication/SiteStreamGrpc/SitestreamGrpc.cs
@@ -53,6 +53,8 @@ namespace ScadaLink.Communication.Grpc {
static readonly grpc::Marshaller __Marshaller_sitestream_AuditEventBatch = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::ScadaLink.Communication.Grpc.AuditEventBatch.Parser));
[global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
static readonly grpc::Marshaller __Marshaller_sitestream_IngestAck = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::ScadaLink.Communication.Grpc.IngestAck.Parser));
+ [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+ static readonly grpc::Marshaller __Marshaller_sitestream_CachedTelemetryBatch = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::ScadaLink.Communication.Grpc.CachedTelemetryBatch.Parser));
[global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
static readonly grpc::Method __Method_SubscribeInstance = new grpc::Method(
@@ -70,6 +72,14 @@ namespace ScadaLink.Communication.Grpc {
__Marshaller_sitestream_AuditEventBatch,
__Marshaller_sitestream_IngestAck);
+ [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+ static readonly grpc::Method __Method_IngestCachedTelemetry = new grpc::Method(
+ grpc::MethodType.Unary,
+ __ServiceName,
+ "IngestCachedTelemetry",
+ __Marshaller_sitestream_CachedTelemetryBatch,
+ __Marshaller_sitestream_IngestAck);
+
/// Service descriptor
public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor
{
@@ -92,6 +102,12 @@ namespace ScadaLink.Communication.Grpc {
throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
}
+ [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+ public virtual global::System.Threading.Tasks.Task IngestCachedTelemetry(global::ScadaLink.Communication.Grpc.CachedTelemetryBatch request, grpc::ServerCallContext context)
+ {
+ throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+ }
+
}
/// Client for SiteStreamService
@@ -151,6 +167,26 @@ namespace ScadaLink.Communication.Grpc {
{
return CallInvoker.AsyncUnaryCall(__Method_IngestAuditEvents, null, options, request);
}
+ [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+ public virtual global::ScadaLink.Communication.Grpc.IngestAck IngestCachedTelemetry(global::ScadaLink.Communication.Grpc.CachedTelemetryBatch request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+ {
+ return IngestCachedTelemetry(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+ }
+ [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+ public virtual global::ScadaLink.Communication.Grpc.IngestAck IngestCachedTelemetry(global::ScadaLink.Communication.Grpc.CachedTelemetryBatch request, grpc::CallOptions options)
+ {
+ return CallInvoker.BlockingUnaryCall(__Method_IngestCachedTelemetry, null, options, request);
+ }
+ [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+ public virtual grpc::AsyncUnaryCall IngestCachedTelemetryAsync(global::ScadaLink.Communication.Grpc.CachedTelemetryBatch request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+ {
+ return IngestCachedTelemetryAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+ }
+ [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+ public virtual grpc::AsyncUnaryCall IngestCachedTelemetryAsync(global::ScadaLink.Communication.Grpc.CachedTelemetryBatch request, grpc::CallOptions options)
+ {
+ return CallInvoker.AsyncUnaryCall(__Method_IngestCachedTelemetry, null, options, request);
+ }
/// Creates a new instance of client from given ClientBaseConfiguration.
[global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
protected override SiteStreamServiceClient NewInstance(ClientBaseConfiguration configuration)
@@ -166,7 +202,8 @@ namespace ScadaLink.Communication.Grpc {
{
return grpc::ServerServiceDefinition.CreateBuilder()
.AddMethod(__Method_SubscribeInstance, serviceImpl.SubscribeInstance)
- .AddMethod(__Method_IngestAuditEvents, serviceImpl.IngestAuditEvents).Build();
+ .AddMethod(__Method_IngestAuditEvents, serviceImpl.IngestAuditEvents)
+ .AddMethod(__Method_IngestCachedTelemetry, serviceImpl.IngestCachedTelemetry).Build();
}
/// Register service method with a service binder with or without implementation. Useful when customizing the service binding logic.
@@ -178,6 +215,7 @@ namespace ScadaLink.Communication.Grpc {
{
serviceBinder.AddMethod(__Method_SubscribeInstance, serviceImpl == null ? null : new grpc::ServerStreamingServerMethod(serviceImpl.SubscribeInstance));
serviceBinder.AddMethod(__Method_IngestAuditEvents, serviceImpl == null ? null : new grpc::UnaryServerMethod(serviceImpl.IngestAuditEvents));
+ serviceBinder.AddMethod(__Method_IngestCachedTelemetry, serviceImpl == null ? null : new grpc::UnaryServerMethod(serviceImpl.IngestCachedTelemetry));
}
}
diff --git a/tests/ScadaLink.Communication.Tests/Protos/CachedTelemetryProtoTests.cs b/tests/ScadaLink.Communication.Tests/Protos/CachedTelemetryProtoTests.cs
new file mode 100644
index 0000000..4405768
--- /dev/null
+++ b/tests/ScadaLink.Communication.Tests/Protos/CachedTelemetryProtoTests.cs
@@ -0,0 +1,173 @@
+using Google.Protobuf;
+using Google.Protobuf.WellKnownTypes;
+using ScadaLink.Communication.Grpc;
+
+namespace ScadaLink.Communication.Tests.Protos;
+
+///
+/// Wire-format round-trip tests for the Audit Log (#23) M3 cached-telemetry
+/// proto messages (,
+/// , ).
+/// Locks the additive contract the central dual-write transaction depends on.
+///
+public class CachedTelemetryProtoTests
+{
+ private static AuditEventDto NewAuditDto(Guid? id = null) => new()
+ {
+ EventId = (id ?? Guid.NewGuid()).ToString(),
+ OccurredAtUtc = Timestamp.FromDateTimeOffset(
+ new DateTimeOffset(2026, 5, 20, 10, 15, 30, 123, TimeSpan.Zero)),
+ Channel = "ApiOutbound",
+ Kind = "CachedSubmit",
+ Status = "Submitted",
+ SourceSiteId = "site-1",
+ };
+
+ [Fact]
+ public void SiteCallOperationalDto_RoundTrip_PreservesAllFields()
+ {
+ var createdAt = Timestamp.FromDateTimeOffset(
+ new DateTimeOffset(2026, 5, 20, 10, 0, 0, TimeSpan.Zero));
+ var updatedAt = Timestamp.FromDateTimeOffset(
+ new DateTimeOffset(2026, 5, 20, 10, 5, 0, TimeSpan.Zero));
+ var terminalAt = Timestamp.FromDateTimeOffset(
+ new DateTimeOffset(2026, 5, 20, 10, 10, 0, TimeSpan.Zero));
+
+ var original = new SiteCallOperationalDto
+ {
+ TrackedOperationId = Guid.NewGuid().ToString(),
+ Channel = "ApiOutbound",
+ Target = "ERP.GetOrder",
+ SourceSite = "site-melbourne",
+ Status = "Delivered",
+ RetryCount = 3,
+ LastError = "transient 503",
+ HttpStatus = 200,
+ CreatedAtUtc = createdAt,
+ UpdatedAtUtc = updatedAt,
+ TerminalAtUtc = terminalAt,
+ };
+
+ var bytes = original.ToByteArray();
+ var deserialized = SiteCallOperationalDto.Parser.ParseFrom(bytes);
+
+ Assert.Equal(original.TrackedOperationId, deserialized.TrackedOperationId);
+ Assert.Equal(original.Channel, deserialized.Channel);
+ Assert.Equal(original.Target, deserialized.Target);
+ Assert.Equal(original.SourceSite, deserialized.SourceSite);
+ Assert.Equal(original.Status, deserialized.Status);
+ Assert.Equal(original.RetryCount, deserialized.RetryCount);
+ Assert.Equal(original.LastError, deserialized.LastError);
+ Assert.Equal(original.HttpStatus, deserialized.HttpStatus);
+ Assert.Equal(original.CreatedAtUtc, deserialized.CreatedAtUtc);
+ Assert.Equal(original.UpdatedAtUtc, deserialized.UpdatedAtUtc);
+ Assert.Equal(original.TerminalAtUtc, deserialized.TerminalAtUtc);
+ }
+
+ [Fact]
+ public void SiteCallOperationalDto_TerminalAt_AbsentWhenNotTerminal()
+ {
+ // Lifecycle events prior to the terminal step leave TerminalAtUtc unset;
+ // the well-known Timestamp wrapper is absent on the wire (null in C#).
+ var dto = new SiteCallOperationalDto
+ {
+ TrackedOperationId = Guid.NewGuid().ToString(),
+ Channel = "DbOutbound",
+ Target = "warehouse.dbo.WriteOrder",
+ SourceSite = "site-brisbane",
+ Status = "Attempted",
+ RetryCount = 1,
+ CreatedAtUtc = Timestamp.FromDateTimeOffset(DateTimeOffset.UtcNow),
+ UpdatedAtUtc = Timestamp.FromDateTimeOffset(DateTimeOffset.UtcNow),
+ };
+
+ Assert.Null(dto.TerminalAtUtc);
+
+ var bytes = dto.ToByteArray();
+ var deserialized = SiteCallOperationalDto.Parser.ParseFrom(bytes);
+
+ Assert.Null(deserialized.TerminalAtUtc);
+ }
+
+ [Fact]
+ public void SiteCallOperationalDto_NullableHttpStatus_AbsentByDefault()
+ {
+ // Int32Value wrapper-typed http_status — unset round-trips as null,
+ // matching DB nullable column semantics for non-API cached writes.
+ var dto = new SiteCallOperationalDto
+ {
+ TrackedOperationId = Guid.NewGuid().ToString(),
+ Channel = "DbOutbound",
+ Target = "warehouse.dbo.WriteOrder",
+ SourceSite = "site-brisbane",
+ Status = "Submitted",
+ RetryCount = 0,
+ CreatedAtUtc = Timestamp.FromDateTimeOffset(DateTimeOffset.UtcNow),
+ UpdatedAtUtc = Timestamp.FromDateTimeOffset(DateTimeOffset.UtcNow),
+ };
+
+ Assert.Null(dto.HttpStatus);
+
+ var bytes = dto.ToByteArray();
+ var deserialized = SiteCallOperationalDto.Parser.ParseFrom(bytes);
+
+ Assert.Null(deserialized.HttpStatus);
+ }
+
+ [Fact]
+ public void CachedTelemetryPacket_RoundTrip_PreservesNestedEntities()
+ {
+ var trackedOpId = Guid.NewGuid().ToString();
+ var auditDto = NewAuditDto();
+ auditDto.Target = "ERP.GetOrder";
+ auditDto.Status = "Attempted";
+
+ var operationalDto = new SiteCallOperationalDto
+ {
+ TrackedOperationId = trackedOpId,
+ Channel = "ApiOutbound",
+ Target = "ERP.GetOrder",
+ SourceSite = "site-1",
+ Status = "Attempted",
+ RetryCount = 2,
+ HttpStatus = 503,
+ LastError = "Service unavailable",
+ CreatedAtUtc = Timestamp.FromDateTimeOffset(DateTimeOffset.UtcNow),
+ UpdatedAtUtc = Timestamp.FromDateTimeOffset(DateTimeOffset.UtcNow),
+ };
+
+ var original = new CachedTelemetryPacket
+ {
+ AuditEvent = auditDto,
+ Operational = operationalDto,
+ };
+
+ var bytes = original.ToByteArray();
+ var deserialized = CachedTelemetryPacket.Parser.ParseFrom(bytes);
+
+ Assert.NotNull(deserialized.AuditEvent);
+ Assert.Equal(auditDto.EventId, deserialized.AuditEvent.EventId);
+ Assert.Equal(auditDto.Target, deserialized.AuditEvent.Target);
+ Assert.Equal(auditDto.Status, deserialized.AuditEvent.Status);
+
+ Assert.NotNull(deserialized.Operational);
+ Assert.Equal(trackedOpId, deserialized.Operational.TrackedOperationId);
+ Assert.Equal(operationalDto.Channel, deserialized.Operational.Channel);
+ Assert.Equal(operationalDto.Status, deserialized.Operational.Status);
+ Assert.Equal(operationalDto.RetryCount, deserialized.Operational.RetryCount);
+ Assert.Equal(operationalDto.HttpStatus, deserialized.Operational.HttpStatus);
+ Assert.Equal(operationalDto.LastError, deserialized.Operational.LastError);
+ }
+
+ [Fact]
+ public void CachedTelemetryBatch_Empty_RoundTrip_Yields_EmptyPackets()
+ {
+ var original = new CachedTelemetryBatch();
+ Assert.Empty(original.Packets);
+
+ var bytes = original.ToByteArray();
+ var deserialized = CachedTelemetryBatch.Parser.ParseFrom(bytes);
+
+ Assert.Empty(deserialized.Packets);
+ }
+}