using System.Collections.Generic; using System.Runtime.Serialization; using System.ServiceModel; using System.Threading; using System.Threading.Tasks; namespace ZB.MOM.WW.LmxProxy.Client.Domain; // ──────────────────────────────────────────────────────────────── // Service contract // ──────────────────────────────────────────────────────────────── /// /// Code-first gRPC service contract for SCADA operations. /// [ServiceContract(Name = "scada.ScadaService")] public interface IScadaService { /// Establishes a connection with the SCADA service. ValueTask ConnectAsync(ConnectRequest request); /// Terminates a SCADA service connection. ValueTask DisconnectAsync(DisconnectRequest request); /// Retrieves the current state of a SCADA connection. ValueTask GetConnectionStateAsync(GetConnectionStateRequest request); /// Reads a single tag value from the SCADA system. ValueTask ReadAsync(ReadRequest request); /// Reads multiple tag values from the SCADA system in a batch operation. ValueTask ReadBatchAsync(ReadBatchRequest request); /// Writes a single value to a tag in the SCADA system. ValueTask WriteAsync(WriteRequest request); /// Writes multiple values to tags in the SCADA system in a batch operation. ValueTask WriteBatchAsync(WriteBatchRequest request); /// Writes multiple values and waits for a completion flag before returning. ValueTask WriteBatchAndWaitAsync(WriteBatchAndWaitRequest request); /// Subscribes to real-time value changes from specified tags. IAsyncEnumerable SubscribeAsync(SubscribeRequest request, CancellationToken cancellationToken = default); /// Validates an API key for authentication. ValueTask CheckApiKeyAsync(CheckApiKeyRequest request); } // ──────────────────────────────────────────────────────────────── // VTQ message // ──────────────────────────────────────────────────────────────── /// /// Value-Timestamp-Quality message transmitted over gRPC. /// All values are string-encoded; timestamps are UTC ticks. /// [DataContract] public class VtqMessage { /// Tag address. [DataMember(Order = 1)] public string Tag { get; set; } = string.Empty; /// Value encoded as a string. [DataMember(Order = 2)] public string Value { get; set; } = string.Empty; /// UTC timestamp as DateTime.Ticks (100ns intervals since 0001-01-01). [DataMember(Order = 3)] public long TimestampUtcTicks { get; set; } /// Quality string: "Good", "Uncertain", or "Bad". [DataMember(Order = 4)] public string Quality { get; set; } = string.Empty; } // ──────────────────────────────────────────────────────────────── // Connect // ──────────────────────────────────────────────────────────────── /// Request to establish a session with the proxy server. [DataContract] public class ConnectRequest { /// Client identifier (e.g., "ScadaLink-{guid}"). [DataMember(Order = 1)] public string ClientId { get; set; } = string.Empty; /// API key for authentication (empty if none required). [DataMember(Order = 2)] public string ApiKey { get; set; } = string.Empty; } /// Response from a Connect call. [DataContract] public class ConnectResponse { /// Whether the connection was established successfully. [DataMember(Order = 1)] public bool Success { get; set; } /// Status or error message. [DataMember(Order = 2)] public string Message { get; set; } = string.Empty; /// Session ID (32-char hex GUID). Only valid when is true. [DataMember(Order = 3)] public string SessionId { get; set; } = string.Empty; } // ──────────────────────────────────────────────────────────────── // Disconnect // ──────────────────────────────────────────────────────────────── /// Request to terminate a session. [DataContract] public class DisconnectRequest { /// Active session ID to disconnect. [DataMember(Order = 1)] public string SessionId { get; set; } = string.Empty; } /// Response from a Disconnect call. [DataContract] public class DisconnectResponse { /// Whether the disconnect succeeded. [DataMember(Order = 1)] public bool Success { get; set; } /// Status or error message. [DataMember(Order = 2)] public string Message { get; set; } = string.Empty; } // ──────────────────────────────────────────────────────────────── // GetConnectionState // ──────────────────────────────────────────────────────────────── /// Request to query connection state for a session. [DataContract] public class GetConnectionStateRequest { /// Session ID to query. [DataMember(Order = 1)] public string SessionId { get; set; } = string.Empty; } /// Response with connection state information. [DataContract] public class GetConnectionStateResponse { /// Whether the session is currently connected. [DataMember(Order = 1)] public bool IsConnected { get; set; } /// Client identifier for this session. [DataMember(Order = 2)] public string ClientId { get; set; } = string.Empty; /// UTC ticks when the connection was established. [DataMember(Order = 3)] public long ConnectedSinceUtcTicks { get; set; } } // ──────────────────────────────────────────────────────────────── // Read // ──────────────────────────────────────────────────────────────── /// Request to read a single tag. [DataContract] public class ReadRequest { /// Valid session ID. [DataMember(Order = 1)] public string SessionId { get; set; } = string.Empty; /// Tag address to read. [DataMember(Order = 2)] public string Tag { get; set; } = string.Empty; } /// Response from a single-tag Read call. [DataContract] public class ReadResponse { /// Whether the read succeeded. [DataMember(Order = 1)] public bool Success { get; set; } /// Error message if the read failed. [DataMember(Order = 2)] public string Message { get; set; } = string.Empty; /// The value-timestamp-quality result. [DataMember(Order = 3)] public VtqMessage? Vtq { get; set; } } // ──────────────────────────────────────────────────────────────── // ReadBatch // ──────────────────────────────────────────────────────────────── /// Request to read multiple tags in a single round-trip. [DataContract] public class ReadBatchRequest { /// Valid session ID. [DataMember(Order = 1)] public string SessionId { get; set; } = string.Empty; /// Tag addresses to read. [DataMember(Order = 2)] public List Tags { get; set; } = []; } /// Response from a batch Read call. [DataContract] public class ReadBatchResponse { /// False if any tag read failed. [DataMember(Order = 1)] public bool Success { get; set; } /// Error message. [DataMember(Order = 2)] public string Message { get; set; } = string.Empty; /// VTQ results in the same order as the request tags. [DataMember(Order = 3)] public List Vtqs { get; set; } = []; } // ──────────────────────────────────────────────────────────────── // Write // ──────────────────────────────────────────────────────────────── /// Request to write a single tag value. [DataContract] public class WriteRequest { /// Valid session ID. [DataMember(Order = 1)] public string SessionId { get; set; } = string.Empty; /// Tag address to write. [DataMember(Order = 2)] public string Tag { get; set; } = string.Empty; /// Value as a string (parsed server-side). [DataMember(Order = 3)] public string Value { get; set; } = string.Empty; } /// Response from a single-tag Write call. [DataContract] public class WriteResponse { /// Whether the write succeeded. [DataMember(Order = 1)] public bool Success { get; set; } /// Status or error message. [DataMember(Order = 2)] public string Message { get; set; } = string.Empty; } // ──────────────────────────────────────────────────────────────── // WriteItem / WriteResult // ──────────────────────────────────────────────────────────────── /// A single tag-value pair for batch write operations. [DataContract] public class WriteItem { /// Tag address. [DataMember(Order = 1)] public string Tag { get; set; } = string.Empty; /// Value as a string. [DataMember(Order = 2)] public string Value { get; set; } = string.Empty; } /// Per-item result from a batch write operation. [DataContract] public class WriteResult { /// Tag address that was written. [DataMember(Order = 1)] public string Tag { get; set; } = string.Empty; /// Whether the individual write succeeded. [DataMember(Order = 2)] public bool Success { get; set; } /// Error message for this item, if any. [DataMember(Order = 3)] public string Message { get; set; } = string.Empty; } // ──────────────────────────────────────────────────────────────── // WriteBatch // ──────────────────────────────────────────────────────────────── /// Request to write multiple tag values in a single round-trip. [DataContract] public class WriteBatchRequest { /// Valid session ID. [DataMember(Order = 1)] public string SessionId { get; set; } = string.Empty; /// Tag-value pairs to write. [DataMember(Order = 2)] public List Items { get; set; } = []; } /// Response from a batch Write call. [DataContract] public class WriteBatchResponse { /// Overall success — false if any item failed. [DataMember(Order = 1)] public bool Success { get; set; } /// Status or error message. [DataMember(Order = 2)] public string Message { get; set; } = string.Empty; /// Per-item write results. [DataMember(Order = 3)] public List Results { get; set; } = []; } // ──────────────────────────────────────────────────────────────── // WriteBatchAndWait // ──────────────────────────────────────────────────────────────── /// /// Request to write multiple tag values then poll a flag tag /// until it matches an expected value or the timeout expires. /// [DataContract] public class WriteBatchAndWaitRequest { /// Valid session ID. [DataMember(Order = 1)] public string SessionId { get; set; } = string.Empty; /// Tag-value pairs to write. [DataMember(Order = 2)] public List Items { get; set; } = []; /// Tag to poll after writes complete. [DataMember(Order = 3)] public string FlagTag { get; set; } = string.Empty; /// Expected value for the flag tag (string comparison). [DataMember(Order = 4)] public string FlagValue { get; set; } = string.Empty; /// Timeout in milliseconds (default 5000 if <= 0). [DataMember(Order = 5)] public int TimeoutMs { get; set; } /// Poll interval in milliseconds (default 100 if <= 0). [DataMember(Order = 6)] public int PollIntervalMs { get; set; } } /// Response from a WriteBatchAndWait call. [DataContract] public class WriteBatchAndWaitResponse { /// Overall operation success. [DataMember(Order = 1)] public bool Success { get; set; } /// Status or error message. [DataMember(Order = 2)] public string Message { get; set; } = string.Empty; /// Per-item write results. [DataMember(Order = 3)] public List WriteResults { get; set; } = []; /// Whether the flag tag matched the expected value before timeout. [DataMember(Order = 4)] public bool FlagReached { get; set; } /// Total elapsed time in milliseconds. [DataMember(Order = 5)] public int ElapsedMs { get; set; } } // ──────────────────────────────────────────────────────────────── // Subscribe // ──────────────────────────────────────────────────────────────── /// Request to subscribe to value change notifications on one or more tags. [DataContract] public class SubscribeRequest { /// Valid session ID. [DataMember(Order = 1)] public string SessionId { get; set; } = string.Empty; /// Tag addresses to monitor. [DataMember(Order = 2)] public List Tags { get; set; } = []; /// Backend sampling interval in milliseconds. [DataMember(Order = 3)] public int SamplingMs { get; set; } } // ──────────────────────────────────────────────────────────────── // CheckApiKey // ──────────────────────────────────────────────────────────────── /// Request to validate an API key without creating a session. [DataContract] public class CheckApiKeyRequest { /// API key to validate. [DataMember(Order = 1)] public string ApiKey { get; set; } = string.Empty; } /// Response from an API key validation check. [DataContract] public class CheckApiKeyResponse { /// Whether the API key is valid. [DataMember(Order = 1)] public bool IsValid { get; set; } /// Validation message. [DataMember(Order = 2)] public string Message { get; set; } = string.Empty; }