Files
scadalink-design/deprecated/lmxproxy/src-reference/ZB.MOM.WW.LmxProxy.Client/Domain/ScadaContracts.cs
Joseph Doherty 9dccf8e72f deprecate(lmxproxy): move all LmxProxy code, tests, and docs to deprecated/
LmxProxy is no longer needed. Moved the entire lmxproxy/ workspace, DCL
adapter files, and related docs to deprecated/. Removed LmxProxy registration
from DataConnectionFactory, project reference from DCL, protocol option from
UI, and cleaned up all requirement docs.
2026-04-08 15:56:23 -04:00

445 lines
17 KiB
C#

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