# Component: GrpcServer ## Purpose The gRPC service implementation that receives client RPCs, validates sessions, and delegates operations to the MxAccessClient. It is the network-facing entry point for all SCADA operations. ## Location `src/ZB.MOM.WW.LmxProxy.Host/Grpc/ScadaGrpcService.cs` — inherits proto-generated `ScadaService.ScadaServiceBase`. ## Responsibilities - Implement all 10 gRPC RPCs defined in `scada.proto`. - Validate session IDs on all data operations before processing. - Delegate read/write/subscribe operations to the MxAccessClient. - Convert between gRPC message types and internal domain types (Vtq, Quality). - Track operation timing and success/failure via PerformanceMetrics. - Handle errors gracefully, returning structured error responses rather than throwing. ## 1. RPC Implementations ### 1.1 Connection Management - **Connect**: Creates a new session via SessionManager if MxAccess is connected. Returns the session ID (32-character hex GUID). Rejects if MxAccess is disconnected. - **Disconnect**: Terminates the session via SessionManager. - **GetConnectionState**: Returns `IsConnected`, `ClientId`, and `ConnectedSinceUtcTicks` from the MxAccessClient. ### 1.2 Read Operations - **Read**: Validates session, applies Polly retry policy, calls MxAccessClient.ReadAsync(), returns VtqMessage. On invalid session, returns a VtqMessage with `Quality.Bad`. - **ReadBatch**: Validates session, reads all tags via MxAccessClient.ReadBatchAsync() with semaphore-controlled concurrency (max 10 concurrent). Returns results in request order. Batch reads are partially successful — individual tags may have Bad quality (with current UTC timestamp) while the overall response succeeds. If a tag read throws an exception, its VTQ is returned with Bad quality. ### 1.3 Write Operations - **Write**: Validates session, parses the string value using the type heuristic, calls MxAccessClient.WriteAsync(). - **WriteBatch**: Validates session, writes all items in parallel via MxAccessClient with semaphore concurrency control. Returns per-item success/failure results. Overall `success` is `false` if any item fails (all-or-nothing at the reporting level). - **WriteBatchAndWait**: Validates session, writes all items first. If any write fails, returns immediately with `success=false`. If writes succeed, polls `flag_tag` at `poll_interval_ms` intervals using type-aware `TypedValueEquals()` comparison (same oneof case required, native type equality, case-sensitive strings, null equals null only). Default timeout: 5000ms, default poll interval: 100ms. If flag matches before timeout: `success=true`, `flag_reached=true`. If timeout expires: `success=true`, `flag_reached=false` (timeout is not an error). Returns `flag_reached` boolean and `elapsed_ms`. ### 1.4 Subscription - **Subscribe**: Validates session (throws `RpcException(Unauthenticated)` on invalid). Creates a subscription handle via SubscriptionManager. Streams VtqMessage items from the subscription channel to the client. Cleans up the subscription on stream cancellation or error. ### 1.5 API Key Check - **CheckApiKey**: Returns validity and role information from the interceptor context. ## 2. Value and Quality Handling ### 2.1 Values (TypedValue) Read responses and subscription updates return values as `TypedValue` (protobuf oneof carrying native types). Write requests receive `TypedValue` and apply the value directly to MxAccess by its native type. If the `oneof` case doesn't match the tag's expected data type, the write returns `WriteResult` with `success=false` indicating type mismatch. No string serialization or parsing heuristics are used. ### 2.2 Quality (QualityCode) Quality is returned as a `QualityCode` message with `uint32 status_code` (OPC UA-compatible) and `string symbolic_name`. The server maps MxAccess quality codes to OPC UA status codes per the quality table in Component-Protocol. Specific error scenarios return specific quality codes (e.g., tag not found → `BadConfigurationError`, comms loss → `BadCommunicationFailure`). ### 2.3 Current Implementation (V1 Legacy) The current codebase still uses v1 string-based encoding. During v2 migration, the following v1 behavior will be removed: - `ConvertValueToString()` — serializes values to strings (bool → lowercase, DateTime → ISO-8601, arrays → JSON, others → `.ToString()`). - `ParseValue()` — parses string values in order: bool → int → long → double → DateTime → raw string. - Three-state string quality mapping: ≥192 → `"Good"`, 64–191 → `"Uncertain"`, <64 → `"Bad"`. ## 3. Error Handling - All RPC methods catch exceptions and return error responses with `success=false` and a descriptive message. Exceptions do not propagate as gRPC status codes (except Subscribe, which throws `RpcException` for invalid sessions). - Each operation is wrapped in a PerformanceMetrics timing scope that records duration and success/failure. ## 4. Session Validation - All data operations (Read, ReadBatch, Write, WriteBatch, WriteBatchAndWait, Subscribe) validate the session ID before processing. - Invalid session on read/write operations returns a response with Bad quality VTQ. - Invalid session on Subscribe throws `RpcException` with `StatusCode.Unauthenticated`. ## Dependencies - **MxAccessClient** (IScadaClient) — all SCADA operations are delegated here. - **SessionManager** — session creation, validation, and termination. - **SubscriptionManager** — subscription lifecycle for the Subscribe RPC. - **PerformanceMetrics** — operation timing and success/failure tracking. ## Interactions - **ApiKeyInterceptor** intercepts all RPCs before they reach ScadaGrpcService, enforcing API key authentication and role-based write authorization. - **SubscriptionManager** provides the channel that Subscribe streams from. - **StatusReportService** reads PerformanceMetrics data that ScadaGrpcService populates.