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;
}