Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6079c62709 | |||
| 37ef27e8ed | |||
| db2218f395 | |||
| bc28fee641 | |||
| 15fceed536 | |||
| afa82e0989 | |||
| b9ef09d26e | |||
| 7d66967122 | |||
| 2f8404d2ef | |||
| 2b92be02b9 | |||
| 056f0d8808 | |||
| 42b0037376 | |||
| de7639a3e9 | |||
| 8738735f0d | |||
| e80f3c70b6 | |||
| 24cc5fd0f0 | |||
| c5153d68bb | |||
| 0e56b5befb | |||
| c5e7479ee4 | |||
| 8a0c59d7e8 | |||
| 828e3e6cf6 | |||
| 7de4efeb02 | |||
| 6f0d142639 | |||
| 11cc6715ed | |||
| f90bff01db | |||
| 6add4b4acc | |||
| 325106920f | |||
| 8aaab82287 | |||
| b3ae200b11 | |||
| 71d2c39f01 | |||
| a68f0cf222 | |||
| 83eba4bec5 | |||
| 10bd0c0e4d | |||
| 865c22a884 | |||
| d48099f0d0 | |||
| bd1d1f1c0e | |||
| 327e9c5f94 | |||
| d2d2e5f68f | |||
| d692232191 | |||
| 65943597d4 | |||
| 27ed65114e | |||
| 397d3c5c4f | |||
| dc9c0c950c | |||
| 867bf18116 |
@@ -116,7 +116,7 @@ External analysis sources referenced by design docs:
|
|||||||
|
|
||||||
Gateway gRPC clients authenticate with an API key in metadata: `authorization: Bearer mxgw_<key-id>_<secret>`. Keys are stored hashed (with a peppered SHA) in a gateway-owned SQLite DB (default `C:\ProgramData\MxGateway\gateway-auth.db`). Scopes (`session`, `invoke`, `event`, `metadata`, `admin`) gate specific RPCs; missing → `Unauthenticated`, insufficient → `PermissionDenied`. The `apikey` subcommand on the server exe manages keys; see `src/MxGateway.Server/Security/Authentication/`.
|
Gateway gRPC clients authenticate with an API key in metadata: `authorization: Bearer mxgw_<key-id>_<secret>`. Keys are stored hashed (with a peppered SHA) in a gateway-owned SQLite DB (default `C:\ProgramData\MxGateway\gateway-auth.db`). Scopes (`session`, `invoke`, `event`, `metadata`, `admin`) gate specific RPCs; missing → `Unauthenticated`, insufficient → `PermissionDenied`. The `apikey` subcommand on the server exe manages keys; see `src/MxGateway.Server/Security/Authentication/`.
|
||||||
|
|
||||||
Dashboard auth uses the same verifier but exchanges the API key for an HTTP-only secure cookie at `/dashboard/login`. `Dashboard:AllowAnonymousLocalhost` bypasses cookie auth on loopback when explicitly enabled.
|
Dashboard auth is LDAP-backed (separate from the gRPC API-key model). `/login` binds against `MxGateway:Ldap` and maps the user's LDAP groups to `Admin` or `Viewer` via `MxGateway:Dashboard:GroupToRole`, then issues an HTTP-only secure `__Host-MxGatewayDashboard` cookie. SignalR hubs at `/hubs/{snapshot,alarms,events}` accept either the cookie or a 30-minute bearer minted at `/hubs/token`. `Dashboard:AllowAnonymousLocalhost` bypasses auth on loopback when enabled.
|
||||||
|
|
||||||
## Process / Platform Notes
|
## Process / Platform Notes
|
||||||
|
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ Recommended layout:
|
|||||||
|
|
||||||
```text
|
```text
|
||||||
clients/dotnet/
|
clients/dotnet/
|
||||||
MxGateway.Client.sln
|
ZB.MOM.WW.MxGateway.Client.slnx
|
||||||
MxGateway.Client/
|
ZB.MOM.WW.MxGateway.Client/
|
||||||
MxGateway.Client.csproj
|
ZB.MOM.WW.MxGateway.Client.csproj
|
||||||
GatewayClient.cs
|
GatewayClient.cs
|
||||||
MxGatewaySession.cs
|
MxGatewaySession.cs
|
||||||
MxGatewayClientOptions.cs
|
MxGatewayClientOptions.cs
|
||||||
@@ -26,14 +26,14 @@ clients/dotnet/
|
|||||||
Conversion/
|
Conversion/
|
||||||
Errors/
|
Errors/
|
||||||
Generated/
|
Generated/
|
||||||
MxGateway.Client.Cli/
|
ZB.MOM.WW.MxGateway.Client.Cli/
|
||||||
MxGateway.Client.Cli.csproj
|
ZB.MOM.WW.MxGateway.Client.Cli.csproj
|
||||||
Program.cs
|
Program.cs
|
||||||
Commands/
|
Commands/
|
||||||
MxGateway.Client.Tests/
|
ZB.MOM.WW.MxGateway.Client.Tests/
|
||||||
MxGateway.Client.Tests.csproj
|
ZB.MOM.WW.MxGateway.Client.Tests.csproj
|
||||||
MxGateway.Client.IntegrationTests/
|
ZB.MOM.WW.MxGateway.Client.IntegrationTests/
|
||||||
MxGateway.Client.IntegrationTests.csproj
|
ZB.MOM.WW.MxGateway.Client.IntegrationTests.csproj
|
||||||
```
|
```
|
||||||
|
|
||||||
Target framework:
|
Target framework:
|
||||||
@@ -43,7 +43,7 @@ Target framework:
|
|||||||
```
|
```
|
||||||
|
|
||||||
The scaffold uses a project reference to
|
The scaffold uses a project reference to
|
||||||
`src/MxGateway.Contracts/MxGateway.Contracts.csproj` for generated protobuf and
|
`src/ZB.MOM.WW.MxGateway.Contracts/ZB.MOM.WW.MxGateway.Contracts.csproj` for generated protobuf and
|
||||||
gRPC types. `clients/dotnet/generated` remains reserved for client-local
|
gRPC types. `clients/dotnet/generated` remains reserved for client-local
|
||||||
generator output if the .NET client later needs to decouple from the contracts
|
generator output if the .NET client later needs to decouple from the contracts
|
||||||
project.
|
project.
|
||||||
@@ -166,7 +166,7 @@ reply.EnsureMxAccessSuccess();
|
|||||||
|
|
||||||
## Test CLI
|
## Test CLI
|
||||||
|
|
||||||
Project: `MxGateway.Client.Cli`.
|
Project: `ZB.MOM.WW.MxGateway.Client.Cli`.
|
||||||
|
|
||||||
Command examples:
|
Command examples:
|
||||||
|
|
||||||
|
|||||||
@@ -1,76 +0,0 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
|
||||||
# Visual Studio Version 17
|
|
||||||
VisualStudioVersion = 17.0.31903.59
|
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MxGateway.Client", "MxGateway.Client\MxGateway.Client.csproj", "{7CF9ED88-1F32-4040-BEB1-D0902E304C70}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MxGateway.Contracts", "..\..\src\MxGateway.Contracts\MxGateway.Contracts.csproj", "{9AB807A8-0469-40F7-A000-D240F36B6E5D}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MxGateway.Client.Cli", "MxGateway.Client.Cli\MxGateway.Client.Cli.csproj", "{EB061E77-2475-4322-9257-3F2456DD141C}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MxGateway.Client.Tests", "MxGateway.Client.Tests\MxGateway.Client.Tests.csproj", "{B77B5A8E-0C53-4419-9BCD-227C9753A074}"
|
|
||||||
EndProject
|
|
||||||
Global
|
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
||||||
Debug|Any CPU = Debug|Any CPU
|
|
||||||
Debug|x64 = Debug|x64
|
|
||||||
Debug|x86 = Debug|x86
|
|
||||||
Release|Any CPU = Release|Any CPU
|
|
||||||
Release|x64 = Release|x64
|
|
||||||
Release|x86 = Release|x86
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
|
||||||
{7CF9ED88-1F32-4040-BEB1-D0902E304C70}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{7CF9ED88-1F32-4040-BEB1-D0902E304C70}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{7CF9ED88-1F32-4040-BEB1-D0902E304C70}.Debug|x64.ActiveCfg = Debug|Any CPU
|
|
||||||
{7CF9ED88-1F32-4040-BEB1-D0902E304C70}.Debug|x64.Build.0 = Debug|Any CPU
|
|
||||||
{7CF9ED88-1F32-4040-BEB1-D0902E304C70}.Debug|x86.ActiveCfg = Debug|Any CPU
|
|
||||||
{7CF9ED88-1F32-4040-BEB1-D0902E304C70}.Debug|x86.Build.0 = Debug|Any CPU
|
|
||||||
{7CF9ED88-1F32-4040-BEB1-D0902E304C70}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{7CF9ED88-1F32-4040-BEB1-D0902E304C70}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{7CF9ED88-1F32-4040-BEB1-D0902E304C70}.Release|x64.ActiveCfg = Release|Any CPU
|
|
||||||
{7CF9ED88-1F32-4040-BEB1-D0902E304C70}.Release|x64.Build.0 = Release|Any CPU
|
|
||||||
{7CF9ED88-1F32-4040-BEB1-D0902E304C70}.Release|x86.ActiveCfg = Release|Any CPU
|
|
||||||
{7CF9ED88-1F32-4040-BEB1-D0902E304C70}.Release|x86.Build.0 = Release|Any CPU
|
|
||||||
{9AB807A8-0469-40F7-A000-D240F36B6E5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{9AB807A8-0469-40F7-A000-D240F36B6E5D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{9AB807A8-0469-40F7-A000-D240F36B6E5D}.Debug|x64.ActiveCfg = Debug|Any CPU
|
|
||||||
{9AB807A8-0469-40F7-A000-D240F36B6E5D}.Debug|x64.Build.0 = Debug|Any CPU
|
|
||||||
{9AB807A8-0469-40F7-A000-D240F36B6E5D}.Debug|x86.ActiveCfg = Debug|Any CPU
|
|
||||||
{9AB807A8-0469-40F7-A000-D240F36B6E5D}.Debug|x86.Build.0 = Debug|Any CPU
|
|
||||||
{9AB807A8-0469-40F7-A000-D240F36B6E5D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{9AB807A8-0469-40F7-A000-D240F36B6E5D}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{9AB807A8-0469-40F7-A000-D240F36B6E5D}.Release|x64.ActiveCfg = Release|Any CPU
|
|
||||||
{9AB807A8-0469-40F7-A000-D240F36B6E5D}.Release|x64.Build.0 = Release|Any CPU
|
|
||||||
{9AB807A8-0469-40F7-A000-D240F36B6E5D}.Release|x86.ActiveCfg = Release|Any CPU
|
|
||||||
{9AB807A8-0469-40F7-A000-D240F36B6E5D}.Release|x86.Build.0 = Release|Any CPU
|
|
||||||
{EB061E77-2475-4322-9257-3F2456DD141C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{EB061E77-2475-4322-9257-3F2456DD141C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{EB061E77-2475-4322-9257-3F2456DD141C}.Debug|x64.ActiveCfg = Debug|Any CPU
|
|
||||||
{EB061E77-2475-4322-9257-3F2456DD141C}.Debug|x64.Build.0 = Debug|Any CPU
|
|
||||||
{EB061E77-2475-4322-9257-3F2456DD141C}.Debug|x86.ActiveCfg = Debug|Any CPU
|
|
||||||
{EB061E77-2475-4322-9257-3F2456DD141C}.Debug|x86.Build.0 = Debug|Any CPU
|
|
||||||
{EB061E77-2475-4322-9257-3F2456DD141C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{EB061E77-2475-4322-9257-3F2456DD141C}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{EB061E77-2475-4322-9257-3F2456DD141C}.Release|x64.ActiveCfg = Release|Any CPU
|
|
||||||
{EB061E77-2475-4322-9257-3F2456DD141C}.Release|x64.Build.0 = Release|Any CPU
|
|
||||||
{EB061E77-2475-4322-9257-3F2456DD141C}.Release|x86.ActiveCfg = Release|Any CPU
|
|
||||||
{EB061E77-2475-4322-9257-3F2456DD141C}.Release|x86.Build.0 = Release|Any CPU
|
|
||||||
{B77B5A8E-0C53-4419-9BCD-227C9753A074}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{B77B5A8E-0C53-4419-9BCD-227C9753A074}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{B77B5A8E-0C53-4419-9BCD-227C9753A074}.Debug|x64.ActiveCfg = Debug|Any CPU
|
|
||||||
{B77B5A8E-0C53-4419-9BCD-227C9753A074}.Debug|x64.Build.0 = Debug|Any CPU
|
|
||||||
{B77B5A8E-0C53-4419-9BCD-227C9753A074}.Debug|x86.ActiveCfg = Debug|Any CPU
|
|
||||||
{B77B5A8E-0C53-4419-9BCD-227C9753A074}.Debug|x86.Build.0 = Debug|Any CPU
|
|
||||||
{B77B5A8E-0C53-4419-9BCD-227C9753A074}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{B77B5A8E-0C53-4419-9BCD-227C9753A074}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{B77B5A8E-0C53-4419-9BCD-227C9753A074}.Release|x64.ActiveCfg = Release|Any CPU
|
|
||||||
{B77B5A8E-0C53-4419-9BCD-227C9753A074}.Release|x64.Build.0 = Release|Any CPU
|
|
||||||
{B77B5A8E-0C53-4419-9BCD-227C9753A074}.Release|x86.ActiveCfg = Release|Any CPU
|
|
||||||
{B77B5A8E-0C53-4419-9BCD-227C9753A074}.Release|x86.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
|
||||||
HideSolutionNode = FALSE
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
[assembly: InternalsVisibleTo("MxGateway.Client.Tests")]
|
|
||||||
+38
-27
@@ -7,11 +7,11 @@ CLI, and unit tests.
|
|||||||
|
|
||||||
| Project | Purpose |
|
| Project | Purpose |
|
||||||
|---------|---------|
|
|---------|---------|
|
||||||
| `MxGateway.Client` | .NET 10 library entry point, raw gRPC calls, and session helpers. |
|
| `ZB.MOM.WW.MxGateway.Client` | .NET 10 library entry point, raw gRPC calls, and session helpers. |
|
||||||
| `MxGateway.Client.Cli` | Test CLI for smoke and diagnostic commands. |
|
| `ZB.MOM.WW.MxGateway.Client.Cli` | Test CLI for smoke and diagnostic commands. |
|
||||||
| `MxGateway.Client.Tests` | Unit tests for client options, generated contract wiring, auth metadata, session helpers, cancellation, and event streaming. |
|
| `ZB.MOM.WW.MxGateway.Client.Tests` | Unit tests for client options, generated contract wiring, auth metadata, session helpers, cancellation, and event streaming. |
|
||||||
|
|
||||||
The projects reference `src/MxGateway.Contracts/MxGateway.Contracts.csproj` so
|
The projects reference `src/ZB.MOM.WW.MxGateway.Contracts/ZB.MOM.WW.MxGateway.Contracts.csproj` so
|
||||||
the client compiles against the same generated protobuf and gRPC types as the
|
the client compiles against the same generated protobuf and gRPC types as the
|
||||||
gateway. `clients/dotnet/generated` remains reserved for generator output if a
|
gateway. `clients/dotnet/generated` remains reserved for generator output if a
|
||||||
future client build switches to client-local `Grpc.Tools` generation.
|
future client build switches to client-local `Grpc.Tools` generation.
|
||||||
@@ -19,8 +19,8 @@ future client build switches to client-local `Grpc.Tools` generation.
|
|||||||
## Build And Test
|
## Build And Test
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
dotnet build clients/dotnet/MxGateway.Client.sln
|
dotnet build clients/dotnet/ZB.MOM.WW.MxGateway.Client.slnx
|
||||||
dotnet test clients/dotnet/MxGateway.Client.sln --no-build
|
dotnet test clients/dotnet/ZB.MOM.WW.MxGateway.Client.slnx --no-build
|
||||||
```
|
```
|
||||||
|
|
||||||
## Packaging
|
## Packaging
|
||||||
@@ -29,8 +29,8 @@ Create local library and CLI artifacts from the repository root:
|
|||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
$dotnetPackageOutput = Join-Path (Get-Location) 'artifacts/clients/dotnet'
|
$dotnetPackageOutput = Join-Path (Get-Location) 'artifacts/clients/dotnet'
|
||||||
dotnet pack clients/dotnet/MxGateway.Client/MxGateway.Client.csproj -c Release -p:PackageOutputPath="$dotnetPackageOutput"
|
dotnet pack clients/dotnet/ZB.MOM.WW.MxGateway.Client/ZB.MOM.WW.MxGateway.Client.csproj -c Release -p:PackageOutputPath="$dotnetPackageOutput"
|
||||||
dotnet publish clients/dotnet/MxGateway.Client.Cli/MxGateway.Client.Cli.csproj -c Release -o artifacts/clients/dotnet/mxgw-dotnet
|
dotnet publish clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli/ZB.MOM.WW.MxGateway.Client.Cli.csproj -c Release -o artifacts/clients/dotnet/mxgw-dotnet
|
||||||
```
|
```
|
||||||
|
|
||||||
The library package references the shared contracts project at build time. The
|
The library package references the shared contracts project at build time. The
|
||||||
@@ -39,11 +39,11 @@ published CLI runs from `artifacts/clients/dotnet/mxgw-dotnet`.
|
|||||||
## Regenerating Protobuf Bindings
|
## Regenerating Protobuf Bindings
|
||||||
|
|
||||||
The .NET client uses the generated C# types from
|
The .NET client uses the generated C# types from
|
||||||
`src/MxGateway.Contracts/Generated`. Regenerate those files through the
|
`src/ZB.MOM.WW.MxGateway.Contracts/Generated`. Regenerate those files through the
|
||||||
contracts project:
|
contracts project:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
dotnet build src/MxGateway.Contracts/MxGateway.Contracts.csproj
|
dotnet build src/ZB.MOM.WW.MxGateway.Contracts/ZB.MOM.WW.MxGateway.Contracts.csproj
|
||||||
```
|
```
|
||||||
|
|
||||||
## Client Usage
|
## Client Usage
|
||||||
@@ -84,6 +84,15 @@ messages. `MxGatewaySession.OpenSessionReply` keeps the raw session-open reply
|
|||||||
available, and command helpers have `*RawAsync` variants when callers need the
|
available, and command helpers have `*RawAsync` variants when callers need the
|
||||||
complete `MxCommandReply`.
|
complete `MxCommandReply`.
|
||||||
|
|
||||||
|
For alarms, the client exposes `QueryActiveAlarmsAsync` (one-shot snapshot of
|
||||||
|
the active alarms the gateway's central monitor currently holds),
|
||||||
|
`StreamAlarmsAsync` (server-streaming feed of alarm-state-change messages
|
||||||
|
keyed by the same monitor), and `AcknowledgeAlarmAsync` (ack by alarm
|
||||||
|
reference, optional comment, ack target). All three accept a cancellation
|
||||||
|
token and pass through the `MxGateway:Alarms` configuration on the
|
||||||
|
server — when alarms are disabled, the gateway returns an empty list / empty
|
||||||
|
stream rather than failing.
|
||||||
|
|
||||||
`MxGatewaySession.CloseAsync` is explicit and idempotent. Repeated calls return
|
`MxGatewaySession.CloseAsync` is explicit and idempotent. Repeated calls return
|
||||||
the first `CloseSessionReply` instead of sending another close request.
|
the first `CloseSessionReply` instead of sending another close request.
|
||||||
|
|
||||||
@@ -117,15 +126,17 @@ reply.
|
|||||||
The test CLI supports deterministic JSON output for automation:
|
The test CLI supports deterministic JSON output for automation:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
dotnet run --project clients/dotnet/MxGateway.Client.Cli -- version --json
|
dotnet run --project clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli -- version --json
|
||||||
dotnet run --project clients/dotnet/MxGateway.Client.Cli -- open-session --endpoint http://localhost:5000 --api-key-env MXGATEWAY_API_KEY --json
|
dotnet run --project clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli -- open-session --endpoint http://localhost:5000 --api-key-env MXGATEWAY_API_KEY --json
|
||||||
dotnet run --project clients/dotnet/MxGateway.Client.Cli -- register --session-id <id> --client-name mxgw-dotnet-cli --json
|
dotnet run --project clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli -- register --session-id <id> --client-name mxgw-dotnet-cli --json
|
||||||
dotnet run --project clients/dotnet/MxGateway.Client.Cli -- add-item --session-id <id> --server-handle 1 --item Area001.Pump001.Speed --json
|
dotnet run --project clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli -- add-item --session-id <id> --server-handle 1 --item Area001.Pump001.Speed --json
|
||||||
dotnet run --project clients/dotnet/MxGateway.Client.Cli -- advise --session-id <id> --server-handle 1 --item-handle 1 --json
|
dotnet run --project clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli -- advise --session-id <id> --server-handle 1 --item-handle 1 --json
|
||||||
dotnet run --project clients/dotnet/MxGateway.Client.Cli -- write --session-id <id> --server-handle 1 --item-handle 1 --type int32 --value 123 --json
|
dotnet run --project clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli -- write --session-id <id> --server-handle 1 --item-handle 1 --type int32 --value 123 --json
|
||||||
dotnet run --project clients/dotnet/MxGateway.Client.Cli -- write2 --session-id <id> --server-handle 1 --item-handle 1 --type int32 --value 123 --timestamp 2026-01-01T00:00:00Z --json
|
dotnet run --project clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli -- write2 --session-id <id> --server-handle 1 --item-handle 1 --type int32 --value 123 --timestamp 2026-01-01T00:00:00Z --json
|
||||||
dotnet run --project clients/dotnet/MxGateway.Client.Cli -- stream-events --session-id <id> --max-events 1 --json
|
dotnet run --project clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli -- stream-events --session-id <id> --max-events 1 --json
|
||||||
dotnet run --project clients/dotnet/MxGateway.Client.Cli -- smoke --endpoint http://localhost:5000 --api-key-env MXGATEWAY_API_KEY --item Area001.Pump001.Speed --json
|
dotnet run --project clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli -- stream-alarms --session-id <id> --max-messages 1 --json
|
||||||
|
dotnet run --project clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli -- acknowledge-alarm --session-id <id> --alarm-reference "\\Galaxy\Area001.Pump001.PumpFault" --json
|
||||||
|
dotnet run --project clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli -- smoke --endpoint http://localhost:5000 --api-key-env MXGATEWAY_API_KEY --item Area001.Pump001.Speed --json
|
||||||
```
|
```
|
||||||
|
|
||||||
`smoke` opens a session, registers a client, adds one item, advises it,
|
`smoke` opens a session, registers a client, adds one item, advises it,
|
||||||
@@ -180,9 +191,9 @@ IReadOnlyList<GalaxyObject> pumps = await repository.DiscoverHierarchyAsync(
|
|||||||
The CLI exposes the same operations:
|
The CLI exposes the same operations:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
dotnet run --project clients/dotnet/MxGateway.Client.Cli -- galaxy-test-connection --endpoint http://localhost:5000 --api-key-env MXGATEWAY_API_KEY --json
|
dotnet run --project clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli -- galaxy-test-connection --endpoint http://localhost:5000 --api-key-env MXGATEWAY_API_KEY --json
|
||||||
dotnet run --project clients/dotnet/MxGateway.Client.Cli -- galaxy-last-deploy --endpoint http://localhost:5000 --api-key-env MXGATEWAY_API_KEY --json
|
dotnet run --project clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli -- galaxy-last-deploy --endpoint http://localhost:5000 --api-key-env MXGATEWAY_API_KEY --json
|
||||||
dotnet run --project clients/dotnet/MxGateway.Client.Cli -- galaxy-discover --endpoint http://localhost:5000 --api-key-env MXGATEWAY_API_KEY
|
dotnet run --project clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli -- galaxy-discover --endpoint http://localhost:5000 --api-key-env MXGATEWAY_API_KEY
|
||||||
```
|
```
|
||||||
|
|
||||||
### Watching deploy events
|
### Watching deploy events
|
||||||
@@ -217,15 +228,15 @@ await foreach (DeployEvent evt in repository.WatchDeployEventsAsync(
|
|||||||
The CLI counterpart streams events until Ctrl+C (or `--max-events`):
|
The CLI counterpart streams events until Ctrl+C (or `--max-events`):
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
dotnet run --project clients/dotnet/MxGateway.Client.Cli -- galaxy-watch --endpoint http://localhost:5000 --api-key-env MXGATEWAY_API_KEY
|
dotnet run --project clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli -- galaxy-watch --endpoint http://localhost:5000 --api-key-env MXGATEWAY_API_KEY
|
||||||
dotnet run --project clients/dotnet/MxGateway.Client.Cli -- galaxy-watch --endpoint http://localhost:5000 --api-key-env MXGATEWAY_API_KEY --last-seen-deploy-time 2026-04-28T14:30:00Z --json
|
dotnet run --project clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli -- galaxy-watch --endpoint http://localhost:5000 --api-key-env MXGATEWAY_API_KEY --last-seen-deploy-time 2026-04-28T14:30:00Z --json
|
||||||
dotnet run --project clients/dotnet/MxGateway.Client.Cli -- galaxy-watch --endpoint http://localhost:5000 --api-key-env MXGATEWAY_API_KEY --max-events 5 --json
|
dotnet run --project clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli -- galaxy-watch --endpoint http://localhost:5000 --api-key-env MXGATEWAY_API_KEY --max-events 5 --json
|
||||||
```
|
```
|
||||||
|
|
||||||
Use TLS options for a secured gateway:
|
Use TLS options for a secured gateway:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
dotnet run --project clients/dotnet/MxGateway.Client.Cli -- smoke --endpoint https://mxgateway.example.local:5001 --tls --ca-file C:\certs\mxgateway-ca.pem --server-name mxgateway.example.local --api-key-env MXGATEWAY_API_KEY --item Area001.Pump001.Speed --json
|
dotnet run --project clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli -- smoke --endpoint https://ZB.MOM.WW.MxGateway.example.local:5001 --tls --ca-file C:\certs\mxgateway-ca.pem --server-name ZB.MOM.WW.MxGateway.example.local --api-key-env MXGATEWAY_API_KEY --item Area001.Pump001.Speed --json
|
||||||
```
|
```
|
||||||
|
|
||||||
## Integration Checks
|
## Integration Checks
|
||||||
@@ -237,7 +248,7 @@ $env:MXGATEWAY_INTEGRATION = '1'
|
|||||||
$env:MXGATEWAY_ENDPOINT = 'http://localhost:5000'
|
$env:MXGATEWAY_ENDPOINT = 'http://localhost:5000'
|
||||||
$env:MXGATEWAY_API_KEY = '<gateway-api-key>'
|
$env:MXGATEWAY_API_KEY = '<gateway-api-key>'
|
||||||
$env:MXGATEWAY_TEST_ITEM = 'Area001.Pump001.Speed'
|
$env:MXGATEWAY_TEST_ITEM = 'Area001.Pump001.Speed'
|
||||||
dotnet run --project clients/dotnet/MxGateway.Client.Cli -- smoke --endpoint $env:MXGATEWAY_ENDPOINT --api-key-env MXGATEWAY_API_KEY --item $env:MXGATEWAY_TEST_ITEM --json
|
dotnet run --project clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli -- smoke --endpoint $env:MXGATEWAY_ENDPOINT --api-key-env MXGATEWAY_API_KEY --item $env:MXGATEWAY_TEST_ITEM --json
|
||||||
```
|
```
|
||||||
|
|
||||||
## Related Documentation
|
## Related Documentation
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
|
||||||
namespace MxGateway.Client.Cli;
|
namespace ZB.MOM.WW.MxGateway.Client.Cli;
|
||||||
|
|
||||||
/// <summary>Parses command-line arguments into flags and named values.</summary>
|
/// <summary>Parses command-line arguments into flags and named values.</summary>
|
||||||
internal sealed class CliArguments
|
internal sealed class CliArguments
|
||||||
+24
-3
@@ -1,7 +1,7 @@
|
|||||||
using MxGateway.Contracts.Proto;
|
using ZB.MOM.WW.MxGateway.Contracts.Proto;
|
||||||
using MxGateway.Contracts.Proto.Galaxy;
|
using ZB.MOM.WW.MxGateway.Contracts.Proto.Galaxy;
|
||||||
|
|
||||||
namespace MxGateway.Client.Cli;
|
namespace ZB.MOM.WW.MxGateway.Client.Cli;
|
||||||
|
|
||||||
public interface IMxGatewayCliClient : IAsyncDisposable
|
public interface IMxGatewayCliClient : IAsyncDisposable
|
||||||
{
|
{
|
||||||
@@ -45,6 +45,27 @@ public interface IMxGatewayCliClient : IAsyncDisposable
|
|||||||
StreamEventsRequest request,
|
StreamEventsRequest request,
|
||||||
CancellationToken cancellationToken);
|
CancellationToken cancellationToken);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Acknowledges an active MXAccess alarm condition through the gateway.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">The acknowledge request — alarm reference, comment, operator user.</param>
|
||||||
|
/// <param name="cancellationToken">Cancellation token for the operation.</param>
|
||||||
|
/// <returns>The acknowledge reply with protocol + native MxStatus.</returns>
|
||||||
|
Task<AcknowledgeAlarmReply> AcknowledgeAlarmAsync(
|
||||||
|
AcknowledgeAlarmRequest request,
|
||||||
|
CancellationToken cancellationToken);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attaches to the gateway's central alarm feed — the current active-alarm
|
||||||
|
/// snapshot followed by live transitions.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">The stream request, optionally scoped by alarm-reference prefix.</param>
|
||||||
|
/// <param name="cancellationToken">Cancellation token for the operation.</param>
|
||||||
|
/// <returns>An async enumerable of alarm feed messages.</returns>
|
||||||
|
IAsyncEnumerable<AlarmFeedMessage> StreamAlarmsAsync(
|
||||||
|
StreamAlarmsRequest request,
|
||||||
|
CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tests connection to the Galaxy Repository.
|
/// Tests connection to the Galaxy Repository.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
+20
-4
@@ -1,8 +1,8 @@
|
|||||||
using MxGateway.Client;
|
using ZB.MOM.WW.MxGateway.Client;
|
||||||
using MxGateway.Contracts.Proto;
|
using ZB.MOM.WW.MxGateway.Contracts.Proto;
|
||||||
using MxGateway.Contracts.Proto.Galaxy;
|
using ZB.MOM.WW.MxGateway.Contracts.Proto.Galaxy;
|
||||||
|
|
||||||
namespace MxGateway.Client.Cli;
|
namespace ZB.MOM.WW.MxGateway.Client.Cli;
|
||||||
|
|
||||||
internal sealed class MxGatewayCliClientAdapter : IMxGatewayCliClient
|
internal sealed class MxGatewayCliClientAdapter : IMxGatewayCliClient
|
||||||
{
|
{
|
||||||
@@ -52,6 +52,22 @@ internal sealed class MxGatewayCliClientAdapter : IMxGatewayCliClient
|
|||||||
return _client.StreamEventsAsync(request, cancellationToken);
|
return _client.StreamEventsAsync(request, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Task<AcknowledgeAlarmReply> AcknowledgeAlarmAsync(
|
||||||
|
AcknowledgeAlarmRequest request,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return _client.AcknowledgeAlarmAsync(request, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IAsyncEnumerable<AlarmFeedMessage> StreamAlarmsAsync(
|
||||||
|
StreamAlarmsRequest request,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return _client.StreamAlarmsAsync(request, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Task<TestConnectionReply> GalaxyTestConnectionAsync(
|
public Task<TestConnectionReply> GalaxyTestConnectionAsync(
|
||||||
TestConnectionRequest request,
|
TestConnectionRequest request,
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace MxGateway.Client.Cli;
|
namespace ZB.MOM.WW.MxGateway.Client.Cli;
|
||||||
|
|
||||||
/// <summary>Utility to redact API keys from error messages for safe output.</summary>
|
/// <summary>Utility to redact API keys from error messages for safe output.</summary>
|
||||||
internal static class MxGatewayCliSecretRedactor
|
internal static class MxGatewayCliSecretRedactor
|
||||||
+732
-31
@@ -1,11 +1,11 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Google.Protobuf;
|
using Google.Protobuf;
|
||||||
using MxGateway.Client;
|
using ZB.MOM.WW.MxGateway.Client;
|
||||||
using MxGateway.Contracts.Proto;
|
using ZB.MOM.WW.MxGateway.Contracts.Proto;
|
||||||
using MxGateway.Contracts.Proto.Galaxy;
|
using ZB.MOM.WW.MxGateway.Contracts.Proto.Galaxy;
|
||||||
|
|
||||||
namespace MxGateway.Client.Cli;
|
namespace ZB.MOM.WW.MxGateway.Client.Cli;
|
||||||
|
|
||||||
/// <summary>Command-line interface for the MXAccess Gateway client, supporting session and command operations.</summary>
|
/// <summary>Command-line interface for the MXAccess Gateway client, supporting session and command operations.</summary>
|
||||||
public static class MxGatewayClientCli
|
public static class MxGatewayClientCli
|
||||||
@@ -16,6 +16,8 @@ public static class MxGatewayClientCli
|
|||||||
|
|
||||||
private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web);
|
private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web);
|
||||||
|
|
||||||
|
private const string BatchEndOfRecord = "__MXGW_BATCH_EOR__";
|
||||||
|
|
||||||
/// <summary>Runs the CLI synchronously with the given arguments, writing output and errors.</summary>
|
/// <summary>Runs the CLI synchronously with the given arguments, writing output and errors.</summary>
|
||||||
/// <param name="args">Command-line arguments (command name followed by options).</param>
|
/// <param name="args">Command-line arguments (command name followed by options).</param>
|
||||||
/// <param name="standardOutput">TextWriter for command output.</param>
|
/// <param name="standardOutput">TextWriter for command output.</param>
|
||||||
@@ -25,7 +27,7 @@ public static class MxGatewayClientCli
|
|||||||
TextWriter standardOutput,
|
TextWriter standardOutput,
|
||||||
TextWriter standardError)
|
TextWriter standardError)
|
||||||
{
|
{
|
||||||
return RunAsync(args, standardOutput, standardError)
|
return RunAsync(args, standardOutput, standardError, clientFactory: null, standardInput: null)
|
||||||
.GetAwaiter()
|
.GetAwaiter()
|
||||||
.GetResult();
|
.GetResult();
|
||||||
}
|
}
|
||||||
@@ -35,11 +37,13 @@ public static class MxGatewayClientCli
|
|||||||
/// <param name="standardOutput">TextWriter for command output.</param>
|
/// <param name="standardOutput">TextWriter for command output.</param>
|
||||||
/// <param name="standardError">TextWriter for error messages.</param>
|
/// <param name="standardError">TextWriter for error messages.</param>
|
||||||
/// <param name="clientFactory">Optional factory to create the gateway client; defaults to MxGatewayClient.Create.</param>
|
/// <param name="clientFactory">Optional factory to create the gateway client; defaults to MxGatewayClient.Create.</param>
|
||||||
|
/// <param name="standardInput">Optional TextReader for batch-mode stdin; defaults to <see cref="Console.In"/>.</param>
|
||||||
public static Task<int> RunAsync(
|
public static Task<int> RunAsync(
|
||||||
string[] args,
|
string[] args,
|
||||||
TextWriter standardOutput,
|
TextWriter standardOutput,
|
||||||
TextWriter standardError,
|
TextWriter standardError,
|
||||||
Func<MxGatewayClientOptions, IMxGatewayCliClient>? clientFactory = null)
|
Func<MxGatewayClientOptions, IMxGatewayCliClient>? clientFactory = null,
|
||||||
|
TextReader? standardInput = null)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(args);
|
ArgumentNullException.ThrowIfNull(args);
|
||||||
ArgumentNullException.ThrowIfNull(standardOutput);
|
ArgumentNullException.ThrowIfNull(standardOutput);
|
||||||
@@ -49,14 +53,17 @@ public static class MxGatewayClientCli
|
|||||||
args,
|
args,
|
||||||
standardOutput,
|
standardOutput,
|
||||||
standardError,
|
standardError,
|
||||||
clientFactory ?? CreateDefaultClient);
|
clientFactory ?? CreateDefaultClient,
|
||||||
|
standardInput ?? Console.In);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<int> RunCoreAsync(
|
private static async Task<int> RunCoreAsync(
|
||||||
string[] args,
|
string[] args,
|
||||||
TextWriter standardOutput,
|
TextWriter standardOutput,
|
||||||
TextWriter standardError,
|
TextWriter standardError,
|
||||||
Func<MxGatewayClientOptions, IMxGatewayCliClient> clientFactory)
|
Func<MxGatewayClientOptions, IMxGatewayCliClient> clientFactory,
|
||||||
|
TextReader standardInput,
|
||||||
|
bool forceJsonErrors = false)
|
||||||
{
|
{
|
||||||
if (args.Length is 0 || IsHelp(args[0]))
|
if (args.Length is 0 || IsHelp(args[0]))
|
||||||
{
|
{
|
||||||
@@ -65,6 +72,12 @@ public static class MxGatewayClientCli
|
|||||||
}
|
}
|
||||||
|
|
||||||
string command = args[0].ToLowerInvariant();
|
string command = args[0].ToLowerInvariant();
|
||||||
|
|
||||||
|
if (command is "batch")
|
||||||
|
{
|
||||||
|
return await RunBatchAsync(standardOutput, clientFactory, standardInput).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
CliArguments arguments = new(args.Skip(1));
|
CliArguments arguments = new(args.Skip(1));
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -101,8 +114,24 @@ public static class MxGatewayClientCli
|
|||||||
.ConfigureAwait(false),
|
.ConfigureAwait(false),
|
||||||
"unsubscribe-bulk" => await UnsubscribeBulkAsync(arguments, client, standardOutput, cancellation.Token)
|
"unsubscribe-bulk" => await UnsubscribeBulkAsync(arguments, client, standardOutput, cancellation.Token)
|
||||||
.ConfigureAwait(false),
|
.ConfigureAwait(false),
|
||||||
|
"read-bulk" => await ReadBulkAsync(arguments, client, standardOutput, cancellation.Token)
|
||||||
|
.ConfigureAwait(false),
|
||||||
|
"write-bulk" => await WriteBulkAsync(arguments, client, standardOutput, cancellation.Token)
|
||||||
|
.ConfigureAwait(false),
|
||||||
|
"write2-bulk" => await Write2BulkAsync(arguments, client, standardOutput, cancellation.Token)
|
||||||
|
.ConfigureAwait(false),
|
||||||
|
"write-secured-bulk" => await WriteSecuredBulkAsync(arguments, client, standardOutput, cancellation.Token)
|
||||||
|
.ConfigureAwait(false),
|
||||||
|
"write-secured2-bulk" => await WriteSecured2BulkAsync(arguments, client, standardOutput, cancellation.Token)
|
||||||
|
.ConfigureAwait(false),
|
||||||
|
"bench-read-bulk" => await BenchReadBulkAsync(arguments, client, standardOutput, cancellation.Token)
|
||||||
|
.ConfigureAwait(false),
|
||||||
"stream-events" => await StreamEventsAsync(arguments, client, standardOutput, cancellation.Token)
|
"stream-events" => await StreamEventsAsync(arguments, client, standardOutput, cancellation.Token)
|
||||||
.ConfigureAwait(false),
|
.ConfigureAwait(false),
|
||||||
|
"stream-alarms" => await StreamAlarmsAsync(arguments, client, standardOutput, cancellation.Token)
|
||||||
|
.ConfigureAwait(false),
|
||||||
|
"acknowledge-alarm" => await AcknowledgeAlarmAsync(arguments, client, standardOutput, cancellation.Token)
|
||||||
|
.ConfigureAwait(false),
|
||||||
"write" => await WriteAsync(arguments, client, standardOutput, cancellation.Token)
|
"write" => await WriteAsync(arguments, client, standardOutput, cancellation.Token)
|
||||||
.ConfigureAwait(false),
|
.ConfigureAwait(false),
|
||||||
"write2" => await Write2Async(arguments, client, standardOutput, cancellation.Token)
|
"write2" => await Write2Async(arguments, client, standardOutput, cancellation.Token)
|
||||||
@@ -125,7 +154,7 @@ public static class MxGatewayClientCli
|
|||||||
string? apiKey = arguments.GetOptional("api-key");
|
string? apiKey = arguments.GetOptional("api-key");
|
||||||
string message = MxGatewayCliSecretRedactor.Redact(exception.Message, apiKey);
|
string message = MxGatewayCliSecretRedactor.Redact(exception.Message, apiKey);
|
||||||
|
|
||||||
if (arguments.HasFlag("json"))
|
if (forceJsonErrors || arguments.HasFlag("json"))
|
||||||
{
|
{
|
||||||
standardError.WriteLine(JsonSerializer.Serialize(
|
standardError.WriteLine(JsonSerializer.Serialize(
|
||||||
new { error = message, type = exception.GetType().Name },
|
new { error = message, type = exception.GetType().Name },
|
||||||
@@ -140,6 +169,86 @@ public static class MxGatewayClientCli
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Runs the CLI in batch mode: reads one command line at a time from
|
||||||
|
/// <paramref name="standardInput"/>, dispatches it through the normal
|
||||||
|
/// routing, writes all output to <paramref name="standardOutput"/>, and
|
||||||
|
/// then appends <see cref="BatchEndOfRecord"/> as a sentinel so the
|
||||||
|
/// caller can delimit command results. Continues on failure; errors are
|
||||||
|
/// written as JSON to <paramref name="standardOutput"/> (not stderr) so
|
||||||
|
/// that the harness sees them inside the same delimited block. Exits 0
|
||||||
|
/// on EOF or empty line.
|
||||||
|
/// </summary>
|
||||||
|
private static async Task<int> RunBatchAsync(
|
||||||
|
TextWriter standardOutput,
|
||||||
|
Func<MxGatewayClientOptions, IMxGatewayCliClient> clientFactory,
|
||||||
|
TextReader standardInput)
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
string? line = await standardInput.ReadLineAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
// EOF or empty line signals clean exit.
|
||||||
|
if (line is null || line.Length is 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split on runs of ASCII whitespace — no quoting support by design.
|
||||||
|
string[] lineArgs = line.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
|
// Per-command output is buffered so we can redirect errors to stdout.
|
||||||
|
using StringWriter commandOutput = new();
|
||||||
|
|
||||||
|
// Errors in batch mode go to stdout (same delimited block), formatted as JSON.
|
||||||
|
// We use a capturing error writer and re-emit through commandOutput after the
|
||||||
|
// command returns, so the EOR sentinel always follows the complete result.
|
||||||
|
using StringWriter commandError = new();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await RunCoreAsync(
|
||||||
|
lineArgs,
|
||||||
|
commandOutput,
|
||||||
|
commandError,
|
||||||
|
clientFactory,
|
||||||
|
standardInput,
|
||||||
|
forceJsonErrors: true)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
// Unexpected exception that escaped RunCoreAsync (shouldn't happen, but be safe).
|
||||||
|
// OperationCanceledException from long-running streaming commands
|
||||||
|
// (e.g. galaxy-watch hit by --timeout) is caught here too — the
|
||||||
|
// batch process must continue with the next command rather than
|
||||||
|
// unwinding.
|
||||||
|
commandError.WriteLine(JsonSerializer.Serialize(
|
||||||
|
new { error = exception.Message, type = exception.GetType().Name },
|
||||||
|
JsonOptions));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write any buffered normal output first.
|
||||||
|
string commandOutputText = commandOutput.ToString();
|
||||||
|
if (commandOutputText.Length > 0)
|
||||||
|
{
|
||||||
|
standardOutput.Write(commandOutputText);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then any error output — in batch mode it belongs on stdout so the harness
|
||||||
|
// sees it inside the delimited record.
|
||||||
|
string commandErrorText = commandError.ToString();
|
||||||
|
if (commandErrorText.Length > 0)
|
||||||
|
{
|
||||||
|
standardOutput.Write(commandErrorText);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the end-of-record sentinel and flush so the harness can unblock.
|
||||||
|
standardOutput.WriteLine(BatchEndOfRecord);
|
||||||
|
await standardOutput.FlushAsync().ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static IMxGatewayCliClient CreateDefaultClient(MxGatewayClientOptions options)
|
private static IMxGatewayCliClient CreateDefaultClient(MxGatewayClientOptions options)
|
||||||
{
|
{
|
||||||
return new MxGatewayCliClientAdapter(MxGatewayClient.Create(options));
|
return new MxGatewayCliClientAdapter(MxGatewayClient.Create(options));
|
||||||
@@ -369,6 +478,451 @@ public static class MxGatewayClientCli
|
|||||||
cancellationToken);
|
cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Task<int> ReadBulkAsync(
|
||||||
|
CliArguments arguments,
|
||||||
|
IMxGatewayCliClient client,
|
||||||
|
TextWriter output,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
ReadBulkCommand command = new()
|
||||||
|
{
|
||||||
|
ServerHandle = arguments.GetInt32("server-handle"),
|
||||||
|
TimeoutMs = (uint)arguments.GetInt32("timeout-ms", 0),
|
||||||
|
};
|
||||||
|
command.TagAddresses.Add(ParseStringList(arguments.GetRequired("items")));
|
||||||
|
|
||||||
|
return InvokeAndWriteAsync(
|
||||||
|
arguments,
|
||||||
|
client,
|
||||||
|
output,
|
||||||
|
new MxCommand
|
||||||
|
{
|
||||||
|
Kind = MxCommandKind.ReadBulk,
|
||||||
|
ReadBulk = command,
|
||||||
|
},
|
||||||
|
cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Task<int> WriteBulkAsync(
|
||||||
|
CliArguments arguments,
|
||||||
|
IMxGatewayCliClient client,
|
||||||
|
TextWriter output,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
WriteBulkCommand command = new()
|
||||||
|
{
|
||||||
|
ServerHandle = arguments.GetInt32("server-handle"),
|
||||||
|
};
|
||||||
|
|
||||||
|
IReadOnlyList<int> handles = ParseInt32List(arguments.GetRequired("item-handles"));
|
||||||
|
IReadOnlyList<MxValue> values = ParseValuesList(arguments);
|
||||||
|
int userId = arguments.GetInt32("user-id", 0);
|
||||||
|
EnsureSameLength(handles.Count, values.Count);
|
||||||
|
|
||||||
|
for (int i = 0; i < handles.Count; i++)
|
||||||
|
{
|
||||||
|
command.Entries.Add(new WriteBulkEntry
|
||||||
|
{
|
||||||
|
ItemHandle = handles[i],
|
||||||
|
Value = values[i],
|
||||||
|
UserId = userId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return InvokeAndWriteAsync(
|
||||||
|
arguments,
|
||||||
|
client,
|
||||||
|
output,
|
||||||
|
new MxCommand
|
||||||
|
{
|
||||||
|
Kind = MxCommandKind.WriteBulk,
|
||||||
|
WriteBulk = command,
|
||||||
|
},
|
||||||
|
cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Task<int> Write2BulkAsync(
|
||||||
|
CliArguments arguments,
|
||||||
|
IMxGatewayCliClient client,
|
||||||
|
TextWriter output,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
Write2BulkCommand command = new()
|
||||||
|
{
|
||||||
|
ServerHandle = arguments.GetInt32("server-handle"),
|
||||||
|
};
|
||||||
|
|
||||||
|
IReadOnlyList<int> handles = ParseInt32List(arguments.GetRequired("item-handles"));
|
||||||
|
IReadOnlyList<MxValue> values = ParseValuesList(arguments);
|
||||||
|
MxValue timestampValue = ParseTimestampValue(arguments);
|
||||||
|
int userId = arguments.GetInt32("user-id", 0);
|
||||||
|
EnsureSameLength(handles.Count, values.Count);
|
||||||
|
|
||||||
|
for (int i = 0; i < handles.Count; i++)
|
||||||
|
{
|
||||||
|
command.Entries.Add(new Write2BulkEntry
|
||||||
|
{
|
||||||
|
ItemHandle = handles[i],
|
||||||
|
Value = values[i],
|
||||||
|
TimestampValue = timestampValue,
|
||||||
|
UserId = userId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return InvokeAndWriteAsync(
|
||||||
|
arguments,
|
||||||
|
client,
|
||||||
|
output,
|
||||||
|
new MxCommand
|
||||||
|
{
|
||||||
|
Kind = MxCommandKind.Write2Bulk,
|
||||||
|
Write2Bulk = command,
|
||||||
|
},
|
||||||
|
cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Task<int> WriteSecuredBulkAsync(
|
||||||
|
CliArguments arguments,
|
||||||
|
IMxGatewayCliClient client,
|
||||||
|
TextWriter output,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
WriteSecuredBulkCommand command = new()
|
||||||
|
{
|
||||||
|
ServerHandle = arguments.GetInt32("server-handle"),
|
||||||
|
};
|
||||||
|
|
||||||
|
IReadOnlyList<int> handles = ParseInt32List(arguments.GetRequired("item-handles"));
|
||||||
|
IReadOnlyList<MxValue> values = ParseValuesList(arguments);
|
||||||
|
int currentUserId = arguments.GetInt32("current-user-id");
|
||||||
|
int verifierUserId = arguments.GetInt32("verifier-user-id", 0);
|
||||||
|
EnsureSameLength(handles.Count, values.Count);
|
||||||
|
|
||||||
|
for (int i = 0; i < handles.Count; i++)
|
||||||
|
{
|
||||||
|
command.Entries.Add(new WriteSecuredBulkEntry
|
||||||
|
{
|
||||||
|
ItemHandle = handles[i],
|
||||||
|
Value = values[i],
|
||||||
|
CurrentUserId = currentUserId,
|
||||||
|
VerifierUserId = verifierUserId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return InvokeAndWriteAsync(
|
||||||
|
arguments,
|
||||||
|
client,
|
||||||
|
output,
|
||||||
|
new MxCommand
|
||||||
|
{
|
||||||
|
Kind = MxCommandKind.WriteSecuredBulk,
|
||||||
|
WriteSecuredBulk = command,
|
||||||
|
},
|
||||||
|
cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Task<int> WriteSecured2BulkAsync(
|
||||||
|
CliArguments arguments,
|
||||||
|
IMxGatewayCliClient client,
|
||||||
|
TextWriter output,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
WriteSecured2BulkCommand command = new()
|
||||||
|
{
|
||||||
|
ServerHandle = arguments.GetInt32("server-handle"),
|
||||||
|
};
|
||||||
|
|
||||||
|
IReadOnlyList<int> handles = ParseInt32List(arguments.GetRequired("item-handles"));
|
||||||
|
IReadOnlyList<MxValue> values = ParseValuesList(arguments);
|
||||||
|
MxValue timestampValue = ParseTimestampValue(arguments);
|
||||||
|
int currentUserId = arguments.GetInt32("current-user-id");
|
||||||
|
int verifierUserId = arguments.GetInt32("verifier-user-id", 0);
|
||||||
|
EnsureSameLength(handles.Count, values.Count);
|
||||||
|
|
||||||
|
for (int i = 0; i < handles.Count; i++)
|
||||||
|
{
|
||||||
|
command.Entries.Add(new WriteSecured2BulkEntry
|
||||||
|
{
|
||||||
|
ItemHandle = handles[i],
|
||||||
|
Value = values[i],
|
||||||
|
TimestampValue = timestampValue,
|
||||||
|
CurrentUserId = currentUserId,
|
||||||
|
VerifierUserId = verifierUserId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return InvokeAndWriteAsync(
|
||||||
|
arguments,
|
||||||
|
client,
|
||||||
|
output,
|
||||||
|
new MxCommand
|
||||||
|
{
|
||||||
|
Kind = MxCommandKind.WriteSecured2Bulk,
|
||||||
|
WriteSecured2Bulk = command,
|
||||||
|
},
|
||||||
|
cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parses the bulk-write CLI's <c>--values</c> list. All entries share
|
||||||
|
/// the single <c>--type</c> argument; the comma-separated values are
|
||||||
|
/// each parsed via <see cref="ParseValue(string, string)"/> on a per-entry basis.
|
||||||
|
/// This keeps the CLI simple for e2e use (one type, N values) — callers
|
||||||
|
/// that need heterogeneous types per entry should drive the library
|
||||||
|
/// directly.
|
||||||
|
/// </summary>
|
||||||
|
private static IReadOnlyList<MxValue> ParseValuesList(CliArguments arguments)
|
||||||
|
{
|
||||||
|
string type = arguments.GetRequired("type");
|
||||||
|
string[] values = ParseStringList(arguments.GetRequired("values")).ToArray();
|
||||||
|
MxValue[] result = new MxValue[values.Length];
|
||||||
|
for (int i = 0; i < values.Length; i++)
|
||||||
|
{
|
||||||
|
result[i] = ParseValue(type, values[i]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EnsureSameLength(int handles, int values)
|
||||||
|
{
|
||||||
|
if (handles != values)
|
||||||
|
{
|
||||||
|
throw new ArgumentException(
|
||||||
|
$"Bulk write requires the same number of --item-handles ({handles}) and --values ({values}).");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cross-language stress benchmark for ReadBulk. Opens its own session,
|
||||||
|
/// subscribes to N tags so the worker's MxAccessValueCache populates from
|
||||||
|
/// real OnDataChange events, then hammers ReadBulk in a tight in-process
|
||||||
|
/// loop with per-call Stopwatch timing. Emits a single JSON object on
|
||||||
|
/// stdout that the scripts/bench-read-bulk.ps1 driver collates across
|
||||||
|
/// all five language clients.
|
||||||
|
/// </summary>
|
||||||
|
private static async Task<int> BenchReadBulkAsync(
|
||||||
|
CliArguments arguments,
|
||||||
|
IMxGatewayCliClient client,
|
||||||
|
TextWriter output,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
int durationSeconds = arguments.GetInt32("duration-seconds", 30);
|
||||||
|
int warmupSeconds = arguments.GetInt32("warmup-seconds", 3);
|
||||||
|
int bulkSize = arguments.GetInt32("bulk-size", 6);
|
||||||
|
int tagStart = arguments.GetInt32("tag-start", 1);
|
||||||
|
string tagPrefix = arguments.GetOptional("tag-prefix") ?? "TestMachine_";
|
||||||
|
string tagAttribute = arguments.GetOptional("tag-attribute") ?? "TestChangingInt";
|
||||||
|
uint timeoutMs = (uint)arguments.GetInt32("timeout-ms", 1500);
|
||||||
|
string clientName = arguments.GetOptional("client-name") ?? "mxgw-dotnet-bench";
|
||||||
|
|
||||||
|
string[] tags = new string[bulkSize];
|
||||||
|
for (int i = 0; i < bulkSize; i++)
|
||||||
|
{
|
||||||
|
// TestMachine_NNN.<attribute>, three-digit machine numbers matching
|
||||||
|
// the existing e2e tag-discovery convention.
|
||||||
|
tags[i] = $"{tagPrefix}{(tagStart + i):D3}.{tagAttribute}";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open + register + subscribe-bulk so the cache populates before the
|
||||||
|
// measurement window opens.
|
||||||
|
OpenSessionReply openReply = await client.OpenSessionAsync(
|
||||||
|
new OpenSessionRequest { ClientSessionName = clientName, ClientCorrelationId = CreateCorrelationId() },
|
||||||
|
cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
string sessionId = openReply.SessionId;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
MxCommandReply registerReply = await InvokeAndEnsureAsync(
|
||||||
|
client,
|
||||||
|
CreateCommandRequest(sessionId, new MxCommand
|
||||||
|
{
|
||||||
|
Kind = MxCommandKind.Register,
|
||||||
|
Register = new RegisterCommand { ClientName = clientName },
|
||||||
|
}),
|
||||||
|
cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
int serverHandle = registerReply.Register?.ServerHandle ?? registerReply.ReturnValue.Int32Value;
|
||||||
|
|
||||||
|
SubscribeBulkCommand subscribe = new() { ServerHandle = serverHandle };
|
||||||
|
subscribe.TagAddresses.Add(tags);
|
||||||
|
MxCommandReply subscribeReply = await InvokeAndEnsureAsync(
|
||||||
|
client,
|
||||||
|
CreateCommandRequest(sessionId, new MxCommand
|
||||||
|
{
|
||||||
|
Kind = MxCommandKind.SubscribeBulk,
|
||||||
|
SubscribeBulk = subscribe,
|
||||||
|
}),
|
||||||
|
cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
int[] itemHandles = subscribeReply.SubscribeBulk?.Results
|
||||||
|
.Where(r => r.WasSuccessful)
|
||||||
|
.Select(r => r.ItemHandle)
|
||||||
|
.ToArray() ?? [];
|
||||||
|
|
||||||
|
// Warm-up: drive the same call shape so the JIT / connection
|
||||||
|
// pipelines settle before the measurement window opens.
|
||||||
|
DateTime warmupDeadline = DateTime.UtcNow + TimeSpan.FromSeconds(warmupSeconds);
|
||||||
|
ReadBulkCommand readBulkCommand = new()
|
||||||
|
{
|
||||||
|
ServerHandle = serverHandle,
|
||||||
|
TimeoutMs = timeoutMs,
|
||||||
|
};
|
||||||
|
readBulkCommand.TagAddresses.Add(tags);
|
||||||
|
MxCommand readBulkMxCommand = new() { Kind = MxCommandKind.ReadBulk, ReadBulk = readBulkCommand };
|
||||||
|
|
||||||
|
while (DateTime.UtcNow < warmupDeadline)
|
||||||
|
{
|
||||||
|
_ = await client.InvokeAsync(
|
||||||
|
CreateCommandRequest(sessionId, readBulkMxCommand),
|
||||||
|
cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Steady state — capture per-call wall latency with a high-res
|
||||||
|
// Stopwatch so the resolution is sub-millisecond on modern Windows.
|
||||||
|
List<double> latencyMillis = new(capacity: 65536);
|
||||||
|
long totalReadResults = 0;
|
||||||
|
long cachedReadResults = 0;
|
||||||
|
int successfulCalls = 0;
|
||||||
|
int failedCalls = 0;
|
||||||
|
DateTime steadyDeadline = DateTime.UtcNow + TimeSpan.FromSeconds(durationSeconds);
|
||||||
|
DateTime steadyStart = DateTime.UtcNow;
|
||||||
|
|
||||||
|
while (DateTime.UtcNow < steadyDeadline)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();
|
||||||
|
MxCommandReply reply;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
reply = await client.InvokeAsync(
|
||||||
|
CreateCommandRequest(sessionId, readBulkMxCommand),
|
||||||
|
cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
sw.Stop();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
sw.Stop();
|
||||||
|
failedCalls++;
|
||||||
|
latencyMillis.Add(sw.Elapsed.TotalMilliseconds);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
latencyMillis.Add(sw.Elapsed.TotalMilliseconds);
|
||||||
|
if (reply.ProtocolStatus?.Code != ProtocolStatusCode.Ok)
|
||||||
|
{
|
||||||
|
failedCalls++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
successfulCalls++;
|
||||||
|
if (reply.ReadBulk is not null)
|
||||||
|
{
|
||||||
|
foreach (BulkReadResult r in reply.ReadBulk.Results)
|
||||||
|
{
|
||||||
|
totalReadResults++;
|
||||||
|
if (r.WasCached)
|
||||||
|
{
|
||||||
|
cachedReadResults++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double steadyElapsedSeconds = (DateTime.UtcNow - steadyStart).TotalSeconds;
|
||||||
|
|
||||||
|
if (itemHandles.Length > 0)
|
||||||
|
{
|
||||||
|
UnsubscribeBulkCommand unsubscribe = new() { ServerHandle = serverHandle };
|
||||||
|
unsubscribe.ItemHandles.Add(itemHandles);
|
||||||
|
_ = await client.InvokeAsync(
|
||||||
|
CreateCommandRequest(sessionId, new MxCommand
|
||||||
|
{
|
||||||
|
Kind = MxCommandKind.UnsubscribeBulk,
|
||||||
|
UnsubscribeBulk = unsubscribe,
|
||||||
|
}),
|
||||||
|
cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
int totalCalls = successfulCalls + failedCalls;
|
||||||
|
double callsPerSecond = steadyElapsedSeconds > 0
|
||||||
|
? totalCalls / steadyElapsedSeconds
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
object stats = new
|
||||||
|
{
|
||||||
|
language = "dotnet",
|
||||||
|
command = "bench-read-bulk",
|
||||||
|
endpoint = arguments.GetOptional("endpoint") ?? "(default)",
|
||||||
|
clientName,
|
||||||
|
bulkSize,
|
||||||
|
durationSeconds,
|
||||||
|
warmupSeconds,
|
||||||
|
durationMs = (long)(steadyElapsedSeconds * 1000),
|
||||||
|
tags,
|
||||||
|
totalCalls,
|
||||||
|
successfulCalls,
|
||||||
|
failedCalls,
|
||||||
|
totalReadResults,
|
||||||
|
cachedReadResults,
|
||||||
|
callsPerSecond = Math.Round(callsPerSecond, 2),
|
||||||
|
latencyMs = new
|
||||||
|
{
|
||||||
|
p50 = Percentile(latencyMillis, 0.50),
|
||||||
|
p95 = Percentile(latencyMillis, 0.95),
|
||||||
|
p99 = Percentile(latencyMillis, 0.99),
|
||||||
|
max = latencyMillis.Count > 0 ? Math.Round(latencyMillis.Max(), 3) : 0,
|
||||||
|
mean = latencyMillis.Count > 0 ? Math.Round(latencyMillis.Average(), 3) : 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
output.WriteLine(JsonSerializer.Serialize(stats, JsonOptions));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await client.CloseSessionAsync(
|
||||||
|
new CloseSessionRequest { SessionId = sessionId, ClientCorrelationId = CreateCorrelationId() },
|
||||||
|
cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Closing the session is best-effort — never let it mask a real bench error.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Computes the requested percentile from an unsorted latency sample using
|
||||||
|
/// nearest-rank with linear interpolation. Rounds to 3 decimal places to
|
||||||
|
/// match the JSON schema the PS driver collates.
|
||||||
|
/// </summary>
|
||||||
|
private static double Percentile(IReadOnlyList<double> sample, double quantile)
|
||||||
|
{
|
||||||
|
if (sample.Count == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double[] sorted = sample.ToArray();
|
||||||
|
Array.Sort(sorted);
|
||||||
|
if (sorted.Length == 1)
|
||||||
|
{
|
||||||
|
return Math.Round(sorted[0], 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
double rank = quantile * (sorted.Length - 1);
|
||||||
|
int lower = (int)Math.Floor(rank);
|
||||||
|
int upper = (int)Math.Ceiling(rank);
|
||||||
|
double fraction = rank - lower;
|
||||||
|
double value = sorted[lower] + (sorted[upper] - sorted[lower]) * fraction;
|
||||||
|
return Math.Round(value, 3);
|
||||||
|
}
|
||||||
|
|
||||||
private static Task<int> WriteAsync(
|
private static Task<int> WriteAsync(
|
||||||
CliArguments arguments,
|
CliArguments arguments,
|
||||||
IMxGatewayCliClient client,
|
IMxGatewayCliClient client,
|
||||||
@@ -447,29 +1001,37 @@ public static class MxGatewayClientCli
|
|||||||
AfterWorkerSequence = arguments.GetUInt64("after-worker-sequence", 0),
|
AfterWorkerSequence = arguments.GetUInt64("after-worker-sequence", 0),
|
||||||
};
|
};
|
||||||
|
|
||||||
await foreach (MxEvent gatewayEvent in client.StreamEventsAsync(request, cancellationToken)
|
try
|
||||||
.WithCancellation(cancellationToken)
|
|
||||||
.ConfigureAwait(false))
|
|
||||||
{
|
{
|
||||||
if (jsonLines)
|
await foreach (MxEvent gatewayEvent in client.StreamEventsAsync(request, cancellationToken)
|
||||||
|
.WithCancellation(cancellationToken)
|
||||||
|
.ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
output.WriteLine(ProtobufJsonFormatter.Format(gatewayEvent));
|
if (jsonLines)
|
||||||
}
|
{
|
||||||
else if (json)
|
output.WriteLine(ProtobufJsonFormatter.Format(gatewayEvent));
|
||||||
{
|
}
|
||||||
events.Add(gatewayEvent);
|
else if (json)
|
||||||
}
|
{
|
||||||
else
|
events.Add(gatewayEvent);
|
||||||
{
|
}
|
||||||
output.WriteLine(ProtobufJsonFormatter.Format(gatewayEvent));
|
else
|
||||||
}
|
{
|
||||||
|
output.WriteLine(ProtobufJsonFormatter.Format(gatewayEvent));
|
||||||
|
}
|
||||||
|
|
||||||
eventCount++;
|
eventCount++;
|
||||||
if (maxEvents > 0 && eventCount >= maxEvents)
|
if (maxEvents > 0 && eventCount >= maxEvents)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
// Client.Dotnet-017: graceful end-of-window completion mode for a
|
||||||
|
// finite-window event collector. Emit aggregate JSON below and exit 0.
|
||||||
|
}
|
||||||
|
|
||||||
if (json && !jsonLines)
|
if (json && !jsonLines)
|
||||||
{
|
{
|
||||||
@@ -481,6 +1043,124 @@ public static class MxGatewayClientCli
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static async Task<int> StreamAlarmsAsync(
|
||||||
|
CliArguments arguments,
|
||||||
|
IMxGatewayCliClient client,
|
||||||
|
TextWriter output,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
uint maxEvents = arguments.GetUInt32("max-events", 0);
|
||||||
|
bool json = arguments.HasFlag("json");
|
||||||
|
bool jsonLines = arguments.HasFlag("jsonl");
|
||||||
|
if (json && !jsonLines && maxEvents is 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("--json stream-alarms requires --max-events to bound aggregate output.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxEvents > MaxAggregateEvents)
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"--max-events cannot exceed {MaxAggregateEvents}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var messages = json && !jsonLines
|
||||||
|
? new List<AlarmFeedMessage>(checked((int)maxEvents))
|
||||||
|
: [];
|
||||||
|
uint messageCount = 0;
|
||||||
|
var request = new StreamAlarmsRequest
|
||||||
|
{
|
||||||
|
ClientCorrelationId = CreateCorrelationId(),
|
||||||
|
AlarmFilterPrefix = arguments.GetOptional("filter-prefix") ?? string.Empty,
|
||||||
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await foreach (AlarmFeedMessage feedMessage in client.StreamAlarmsAsync(request, cancellationToken)
|
||||||
|
.WithCancellation(cancellationToken)
|
||||||
|
.ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
if (jsonLines)
|
||||||
|
{
|
||||||
|
output.WriteLine(ProtobufJsonFormatter.Format(feedMessage));
|
||||||
|
}
|
||||||
|
else if (json)
|
||||||
|
{
|
||||||
|
messages.Add(feedMessage);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
output.WriteLine(FormatAlarmFeedMessage(feedMessage));
|
||||||
|
}
|
||||||
|
|
||||||
|
messageCount++;
|
||||||
|
if (maxEvents > 0 && messageCount >= maxEvents)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
// Mirrors stream-events (Client.Dotnet-017): the supplied token covers
|
||||||
|
// the user's --timeout wall-clock budget and external Ctrl+C / parent
|
||||||
|
// CTS cancellation. All are graceful completion modes for a
|
||||||
|
// finite-window alarm-feed collector: emit what arrived and exit 0.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json && !jsonLines)
|
||||||
|
{
|
||||||
|
output.WriteLine(JsonSerializer.Serialize(
|
||||||
|
new { alarms = messages.Select(AlarmFeedMessageToJsonElement).ToArray() },
|
||||||
|
JsonOptions));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Task<int> AcknowledgeAlarmAsync(
|
||||||
|
CliArguments arguments,
|
||||||
|
IMxGatewayCliClient client,
|
||||||
|
TextWriter output,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var request = new AcknowledgeAlarmRequest
|
||||||
|
{
|
||||||
|
ClientCorrelationId = CreateCorrelationId(),
|
||||||
|
AlarmFullReference = arguments.GetRequired("reference"),
|
||||||
|
Comment = arguments.GetOptional("comment") ?? string.Empty,
|
||||||
|
OperatorUser = arguments.GetOptional("operator") ?? string.Empty,
|
||||||
|
};
|
||||||
|
|
||||||
|
return WriteReplyAsync(
|
||||||
|
client.AcknowledgeAlarmAsync(request, cancellationToken),
|
||||||
|
arguments,
|
||||||
|
output);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Renders one <see cref="AlarmFeedMessage"/> for the human-readable
|
||||||
|
/// (non-JSON) stream-alarms output, distinguishing the <c>payload</c> oneof
|
||||||
|
/// arms: a snapshot active alarm, the snapshot-complete sentinel, or a live
|
||||||
|
/// transition.
|
||||||
|
/// </summary>
|
||||||
|
private static string FormatAlarmFeedMessage(AlarmFeedMessage feedMessage)
|
||||||
|
{
|
||||||
|
return feedMessage.PayloadCase switch
|
||||||
|
{
|
||||||
|
AlarmFeedMessage.PayloadOneofCase.ActiveAlarm =>
|
||||||
|
$"active-alarm {ProtobufJsonFormatter.Format(feedMessage.ActiveAlarm)}",
|
||||||
|
AlarmFeedMessage.PayloadOneofCase.SnapshotComplete =>
|
||||||
|
$"snapshot-complete {feedMessage.SnapshotComplete}",
|
||||||
|
AlarmFeedMessage.PayloadOneofCase.Transition =>
|
||||||
|
$"transition {ProtobufJsonFormatter.Format(feedMessage.Transition)}",
|
||||||
|
_ => $"unknown-payload {feedMessage.PayloadCase}",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static JsonElement AlarmFeedMessageToJsonElement(AlarmFeedMessage feedMessage)
|
||||||
|
{
|
||||||
|
return JsonDocument.Parse(ProtobufJsonFormatter.Format(feedMessage)).RootElement.Clone();
|
||||||
|
}
|
||||||
|
|
||||||
private static async Task<int> SmokeAsync(
|
private static async Task<int> SmokeAsync(
|
||||||
CliArguments arguments,
|
CliArguments arguments,
|
||||||
IMxGatewayCliClient client,
|
IMxGatewayCliClient client,
|
||||||
@@ -755,11 +1435,15 @@ public static class MxGatewayClientCli
|
|||||||
|
|
||||||
private static MxValue ParseValue(CliArguments arguments)
|
private static MxValue ParseValue(CliArguments arguments)
|
||||||
{
|
{
|
||||||
string type = arguments.GetRequired("type").ToLowerInvariant();
|
return ParseValue(arguments.GetRequired("type"), arguments.GetRequired("value"));
|
||||||
string value = arguments.GetRequired("value");
|
}
|
||||||
|
|
||||||
|
private static MxValue ParseValue(string type, string value)
|
||||||
|
{
|
||||||
|
string normalisedType = type.ToLowerInvariant();
|
||||||
string[] values = value.Split(',', StringSplitOptions.TrimEntries);
|
string[] values = value.Split(',', StringSplitOptions.TrimEntries);
|
||||||
|
|
||||||
return type switch
|
return normalisedType switch
|
||||||
{
|
{
|
||||||
"bool" or "boolean" => bool.Parse(value).ToMxValue(),
|
"bool" or "boolean" => bool.Parse(value).ToMxValue(),
|
||||||
"bool-array" or "boolean-array" => values.Select(bool.Parse).ToArray().ToMxValue(),
|
"bool-array" or "boolean-array" => values.Select(bool.Parse).ToArray().ToMxValue(),
|
||||||
@@ -778,7 +1462,7 @@ public static class MxGatewayClientCli
|
|||||||
.Select(item => DateTimeOffset.Parse(item, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal))
|
.Select(item => DateTimeOffset.Parse(item, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal))
|
||||||
.ToArray()
|
.ToArray()
|
||||||
.ToMxValue(),
|
.ToMxValue(),
|
||||||
_ => throw new ArgumentException($"Unsupported MX value type '{type}'."),
|
_ => throw new ArgumentException($"Unsupported MX value type '{normalisedType}'."),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -989,7 +1673,15 @@ public static class MxGatewayClientCli
|
|||||||
or "advise"
|
or "advise"
|
||||||
or "subscribe-bulk"
|
or "subscribe-bulk"
|
||||||
or "unsubscribe-bulk"
|
or "unsubscribe-bulk"
|
||||||
|
or "read-bulk"
|
||||||
|
or "write-bulk"
|
||||||
|
or "write2-bulk"
|
||||||
|
or "write-secured-bulk"
|
||||||
|
or "write-secured2-bulk"
|
||||||
|
or "bench-read-bulk"
|
||||||
or "stream-events"
|
or "stream-events"
|
||||||
|
or "stream-alarms"
|
||||||
|
or "acknowledge-alarm"
|
||||||
or "write"
|
or "write"
|
||||||
or "write2"
|
or "write2"
|
||||||
or "smoke"
|
or "smoke"
|
||||||
@@ -1032,6 +1724,7 @@ public static class MxGatewayClientCli
|
|||||||
|
|
||||||
private static void WriteUsage(TextWriter writer)
|
private static void WriteUsage(TextWriter writer)
|
||||||
{
|
{
|
||||||
|
writer.WriteLine("mxgw-dotnet batch (reads commands from stdin; writes output + __MXGW_BATCH_EOR__ after each)");
|
||||||
writer.WriteLine("mxgw-dotnet version [--json]");
|
writer.WriteLine("mxgw-dotnet version [--json]");
|
||||||
writer.WriteLine("mxgw-dotnet ping --session-id <id> [--json]");
|
writer.WriteLine("mxgw-dotnet ping --session-id <id> [--json]");
|
||||||
writer.WriteLine("mxgw-dotnet open-session [--client-name <name>] [--json]");
|
writer.WriteLine("mxgw-dotnet open-session [--client-name <name>] [--json]");
|
||||||
@@ -1041,7 +1734,15 @@ public static class MxGatewayClientCli
|
|||||||
writer.WriteLine("mxgw-dotnet advise --session-id <id> --server-handle <n> --item-handle <n> [--json]");
|
writer.WriteLine("mxgw-dotnet advise --session-id <id> --server-handle <n> --item-handle <n> [--json]");
|
||||||
writer.WriteLine("mxgw-dotnet subscribe-bulk --session-id <id> --server-handle <n> --items <ref,ref> [--json]");
|
writer.WriteLine("mxgw-dotnet subscribe-bulk --session-id <id> --server-handle <n> --items <ref,ref> [--json]");
|
||||||
writer.WriteLine("mxgw-dotnet unsubscribe-bulk --session-id <id> --server-handle <n> --item-handles <n,n> [--json]");
|
writer.WriteLine("mxgw-dotnet unsubscribe-bulk --session-id <id> --server-handle <n> --item-handles <n,n> [--json]");
|
||||||
|
writer.WriteLine("mxgw-dotnet read-bulk --session-id <id> --server-handle <n> --items <ref,ref> [--timeout-ms <n>] [--json]");
|
||||||
|
writer.WriteLine("mxgw-dotnet write-bulk --session-id <id> --server-handle <n> --item-handles <n,n> --type <type> --values <v,v> [--user-id <n>] [--json]");
|
||||||
|
writer.WriteLine("mxgw-dotnet write2-bulk --session-id <id> --server-handle <n> --item-handles <n,n> --type <type> --values <v,v> [--timestamp <iso>] [--user-id <n>] [--json]");
|
||||||
|
writer.WriteLine("mxgw-dotnet write-secured-bulk --session-id <id> --server-handle <n> --item-handles <n,n> --type <type> --values <v,v> --current-user-id <n> [--verifier-user-id <n>] [--json]");
|
||||||
|
writer.WriteLine("mxgw-dotnet write-secured2-bulk --session-id <id> --server-handle <n> --item-handles <n,n> --type <type> --values <v,v> --current-user-id <n> [--verifier-user-id <n>] [--timestamp <iso>] [--json]");
|
||||||
|
writer.WriteLine("mxgw-dotnet bench-read-bulk [--duration-seconds <n>] [--warmup-seconds <n>] [--bulk-size <n>] [--tag-start <n>] [--tag-prefix <s>] [--tag-attribute <s>] [--timeout-ms <n>] [--client-name <name>]");
|
||||||
writer.WriteLine("mxgw-dotnet stream-events --session-id <id> [--max-events <n>] [--json]");
|
writer.WriteLine("mxgw-dotnet stream-events --session-id <id> [--max-events <n>] [--json]");
|
||||||
|
writer.WriteLine("mxgw-dotnet stream-alarms [--filter-prefix <ref>] [--max-events <n>] [--json] [--jsonl]");
|
||||||
|
writer.WriteLine("mxgw-dotnet acknowledge-alarm --reference <ref> [--comment <text>] [--operator <user>] [--json]");
|
||||||
writer.WriteLine("mxgw-dotnet write --session-id <id> --server-handle <n> --item-handle <n> --type <type> --value <value> [--json]");
|
writer.WriteLine("mxgw-dotnet write --session-id <id> --server-handle <n> --item-handle <n> --type <type> --value <value> [--json]");
|
||||||
writer.WriteLine("mxgw-dotnet write2 --session-id <id> --server-handle <n> --item-handle <n> --type <type> --value <value> [--timestamp <iso>] [--json]");
|
writer.WriteLine("mxgw-dotnet write2 --session-id <id> --server-handle <n> --item-handle <n> --type <type> --value <value> [--timestamp <iso>] [--json]");
|
||||||
writer.WriteLine("mxgw-dotnet smoke --item <ref> [--value <value> --type <type>] [--json]");
|
writer.WriteLine("mxgw-dotnet smoke --item <ref> [--value <value> --type <type>] [--json]");
|
||||||
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
using MxGateway.Client.Cli;
|
using ZB.MOM.WW.MxGateway.Client.Cli;
|
||||||
|
|
||||||
return await MxGatewayClientCli.RunAsync(args, Console.Out, Console.Error);
|
return await MxGatewayClientCli.RunAsync(args, Console.Out, Console.Error);
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\MxGateway.Client\MxGateway.Client.csproj" />
|
<ProjectReference Include="..\ZB.MOM.WW.MxGateway.Client\ZB.MOM.WW.MxGateway.Client.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
using Grpc.Core;
|
using Grpc.Core;
|
||||||
using MxGateway.Contracts.Proto.Galaxy;
|
using ZB.MOM.WW.MxGateway.Contracts.Proto.Galaxy;
|
||||||
|
|
||||||
namespace MxGateway.Client.Tests;
|
namespace ZB.MOM.WW.MxGateway.Client.Tests;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fake Galaxy Repository client transport for testing.
|
/// Fake Galaxy Repository client transport for testing.
|
||||||
+31
-3
@@ -1,7 +1,7 @@
|
|||||||
using Grpc.Core;
|
using Grpc.Core;
|
||||||
using MxGateway.Contracts.Proto;
|
using ZB.MOM.WW.MxGateway.Contracts.Proto;
|
||||||
|
|
||||||
namespace MxGateway.Client.Tests;
|
namespace ZB.MOM.WW.MxGateway.Client.Tests;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fake implementation of IMxGatewayClientTransport for testing.
|
/// Fake implementation of IMxGatewayClientTransport for testing.
|
||||||
@@ -51,6 +51,11 @@ internal sealed class FakeGatewayTransport(MxGatewayClientOptions options) : IMx
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public List<(QueryActiveAlarmsRequest Request, CallOptions CallOptions)> QueryActiveAlarmsCalls { get; } = [];
|
public List<(QueryActiveAlarmsRequest Request, CallOptions CallOptions)> QueryActiveAlarmsCalls { get; } = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the list of captured StreamAlarmsAsync calls.
|
||||||
|
/// </summary>
|
||||||
|
public List<(StreamAlarmsRequest Request, CallOptions CallOptions)> StreamAlarmsCalls { get; } = [];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the queue of exceptions to throw from AcknowledgeAlarmAsync.
|
/// Gets the queue of exceptions to throw from AcknowledgeAlarmAsync.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -58,6 +63,7 @@ internal sealed class FakeGatewayTransport(MxGatewayClientOptions options) : IMx
|
|||||||
|
|
||||||
private readonly Queue<AcknowledgeAlarmReply> _acknowledgeReplies = new();
|
private readonly Queue<AcknowledgeAlarmReply> _acknowledgeReplies = new();
|
||||||
private readonly List<ActiveAlarmSnapshot> _activeAlarmSnapshots = [];
|
private readonly List<ActiveAlarmSnapshot> _activeAlarmSnapshots = [];
|
||||||
|
private readonly List<AlarmFeedMessage> _alarmFeedMessages = [];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the reply to return from OpenSessionAsync.
|
/// Gets or sets the reply to return from OpenSessionAsync.
|
||||||
@@ -204,7 +210,6 @@ internal sealed class FakeGatewayTransport(MxGatewayClientOptions options) : IMx
|
|||||||
? _acknowledgeReplies.Dequeue()
|
? _acknowledgeReplies.Dequeue()
|
||||||
: new AcknowledgeAlarmReply
|
: new AcknowledgeAlarmReply
|
||||||
{
|
{
|
||||||
SessionId = request.SessionId,
|
|
||||||
CorrelationId = request.ClientCorrelationId,
|
CorrelationId = request.ClientCorrelationId,
|
||||||
ProtocolStatus = new ProtocolStatus { Code = ProtocolStatusCode.Ok },
|
ProtocolStatus = new ProtocolStatus { Code = ProtocolStatusCode.Ok },
|
||||||
Status = new MxStatusProxy { Success = 1, Category = MxStatusCategory.Ok },
|
Status = new MxStatusProxy { Success = 1, Category = MxStatusCategory.Ok },
|
||||||
@@ -239,4 +244,27 @@ internal sealed class FakeGatewayTransport(MxGatewayClientOptions options) : IMx
|
|||||||
{
|
{
|
||||||
_activeAlarmSnapshots.Add(snapshot);
|
_activeAlarmSnapshots.Add(snapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Records the stream-alarms call and yields each enqueued feed message.
|
||||||
|
/// </summary>
|
||||||
|
public async IAsyncEnumerable<AlarmFeedMessage> StreamAlarmsAsync(
|
||||||
|
StreamAlarmsRequest request,
|
||||||
|
CallOptions callOptions)
|
||||||
|
{
|
||||||
|
StreamAlarmsCalls.Add((request, callOptions));
|
||||||
|
|
||||||
|
foreach (AlarmFeedMessage message in _alarmFeedMessages)
|
||||||
|
{
|
||||||
|
callOptions.CancellationToken.ThrowIfCancellationRequested();
|
||||||
|
await Task.Yield();
|
||||||
|
yield return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Enqueues an alarm feed message to be yielded from StreamAlarmsAsync.</summary>
|
||||||
|
public void AddAlarmFeedMessage(AlarmFeedMessage message)
|
||||||
|
{
|
||||||
|
_alarmFeedMessages.Add(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
using Google.Protobuf.WellKnownTypes;
|
using Google.Protobuf.WellKnownTypes;
|
||||||
using Grpc.Core;
|
using Grpc.Core;
|
||||||
using MxGateway.Contracts.Proto.Galaxy;
|
using ZB.MOM.WW.MxGateway.Contracts.Proto.Galaxy;
|
||||||
|
|
||||||
namespace MxGateway.Client.Tests;
|
namespace ZB.MOM.WW.MxGateway.Client.Tests;
|
||||||
|
|
||||||
public sealed class GalaxyRepositoryClientTests
|
public sealed class GalaxyRepositoryClientTests
|
||||||
{
|
{
|
||||||
+3
-3
@@ -1,8 +1,8 @@
|
|||||||
using Google.Protobuf;
|
using Google.Protobuf;
|
||||||
using MxGateway.Client;
|
using ZB.MOM.WW.MxGateway.Client;
|
||||||
using MxGateway.Contracts.Proto;
|
using ZB.MOM.WW.MxGateway.Contracts.Proto;
|
||||||
|
|
||||||
namespace MxGateway.Client.Tests;
|
namespace ZB.MOM.WW.MxGateway.Client.Tests;
|
||||||
|
|
||||||
public sealed class MxCommandReplyExtensionsTests
|
public sealed class MxCommandReplyExtensionsTests
|
||||||
{
|
{
|
||||||
+2
-6
@@ -1,8 +1,8 @@
|
|||||||
using Google.Protobuf.WellKnownTypes;
|
using Google.Protobuf.WellKnownTypes;
|
||||||
using Grpc.Core;
|
using Grpc.Core;
|
||||||
using MxGateway.Contracts.Proto;
|
using ZB.MOM.WW.MxGateway.Contracts.Proto;
|
||||||
|
|
||||||
namespace MxGateway.Client.Tests;
|
namespace ZB.MOM.WW.MxGateway.Client.Tests;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// PR E.2 — pins the .NET SDK surface for the new alarm RPCs:
|
/// PR E.2 — pins the .NET SDK surface for the new alarm RPCs:
|
||||||
@@ -17,7 +17,6 @@ public sealed class MxGatewayClientAlarmsTests
|
|||||||
FakeGatewayTransport transport = CreateTransport();
|
FakeGatewayTransport transport = CreateTransport();
|
||||||
transport.AddAcknowledgeReply(new AcknowledgeAlarmReply
|
transport.AddAcknowledgeReply(new AcknowledgeAlarmReply
|
||||||
{
|
{
|
||||||
SessionId = "session-fixture",
|
|
||||||
CorrelationId = "corr-1",
|
CorrelationId = "corr-1",
|
||||||
ProtocolStatus = new ProtocolStatus { Code = ProtocolStatusCode.Ok },
|
ProtocolStatus = new ProtocolStatus { Code = ProtocolStatusCode.Ok },
|
||||||
Status = new MxStatusProxy
|
Status = new MxStatusProxy
|
||||||
@@ -31,7 +30,6 @@ public sealed class MxGatewayClientAlarmsTests
|
|||||||
|
|
||||||
AcknowledgeAlarmReply reply = await client.AcknowledgeAlarmAsync(new AcknowledgeAlarmRequest
|
AcknowledgeAlarmReply reply = await client.AcknowledgeAlarmAsync(new AcknowledgeAlarmRequest
|
||||||
{
|
{
|
||||||
SessionId = "session-fixture",
|
|
||||||
ClientCorrelationId = "corr-1",
|
ClientCorrelationId = "corr-1",
|
||||||
AlarmFullReference = "Tank01.Level.HiHi",
|
AlarmFullReference = "Tank01.Level.HiHi",
|
||||||
Comment = "investigating",
|
Comment = "investigating",
|
||||||
@@ -64,7 +62,6 @@ public sealed class MxGatewayClientAlarmsTests
|
|||||||
client.AcknowledgeAlarmAsync(
|
client.AcknowledgeAlarmAsync(
|
||||||
new AcknowledgeAlarmRequest
|
new AcknowledgeAlarmRequest
|
||||||
{
|
{
|
||||||
SessionId = "session-fixture",
|
|
||||||
AlarmFullReference = "Tank01.Level.HiHi",
|
AlarmFullReference = "Tank01.Level.HiHi",
|
||||||
Comment = string.Empty,
|
Comment = string.Empty,
|
||||||
OperatorUser = "alice",
|
OperatorUser = "alice",
|
||||||
@@ -89,7 +86,6 @@ public sealed class MxGatewayClientAlarmsTests
|
|||||||
var ex = await Assert.ThrowsAsync<RpcException>(
|
var ex = await Assert.ThrowsAsync<RpcException>(
|
||||||
() => client.AcknowledgeAlarmAsync(new AcknowledgeAlarmRequest
|
() => client.AcknowledgeAlarmAsync(new AcknowledgeAlarmRequest
|
||||||
{
|
{
|
||||||
SessionId = "session-fixture",
|
|
||||||
AlarmFullReference = "Tank01.Level.HiHi",
|
AlarmFullReference = "Tank01.Level.HiHi",
|
||||||
Comment = string.Empty,
|
Comment = string.Empty,
|
||||||
OperatorUser = "alice",
|
OperatorUser = "alice",
|
||||||
+180
-4
@@ -1,9 +1,9 @@
|
|||||||
using Google.Protobuf.WellKnownTypes;
|
using Google.Protobuf.WellKnownTypes;
|
||||||
using MxGateway.Client.Cli;
|
using ZB.MOM.WW.MxGateway.Client.Cli;
|
||||||
using MxGateway.Contracts.Proto;
|
using ZB.MOM.WW.MxGateway.Contracts.Proto;
|
||||||
using MxGateway.Contracts.Proto.Galaxy;
|
using ZB.MOM.WW.MxGateway.Contracts.Proto.Galaxy;
|
||||||
|
|
||||||
namespace MxGateway.Client.Tests;
|
namespace ZB.MOM.WW.MxGateway.Client.Tests;
|
||||||
|
|
||||||
/// <summary>Tests for the CLI command interface.</summary>
|
/// <summary>Tests for the CLI command interface.</summary>
|
||||||
public sealed class MxGatewayClientCliTests
|
public sealed class MxGatewayClientCliTests
|
||||||
@@ -148,6 +148,87 @@ public sealed class MxGatewayClientCliTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>Verifies that stream-alarms with --max-events stops output and distinguishes payload cases.</summary>
|
||||||
|
[Fact]
|
||||||
|
public async Task RunAsync_StreamAlarms_WithMaxEventsStopsAndDistinguishesPayloadCases()
|
||||||
|
{
|
||||||
|
using var output = new StringWriter();
|
||||||
|
using var error = new StringWriter();
|
||||||
|
FakeCliClient fakeClient = new();
|
||||||
|
fakeClient.AlarmFeedMessages.Add(new AlarmFeedMessage
|
||||||
|
{
|
||||||
|
ActiveAlarm = new ActiveAlarmSnapshot { AlarmFullReference = "Tank01.Level.HiHi" },
|
||||||
|
});
|
||||||
|
fakeClient.AlarmFeedMessages.Add(new AlarmFeedMessage { SnapshotComplete = true });
|
||||||
|
|
||||||
|
int exitCode = await MxGatewayClientCli.RunAsync(
|
||||||
|
[
|
||||||
|
"stream-alarms",
|
||||||
|
"--endpoint",
|
||||||
|
"http://localhost:5000",
|
||||||
|
"--api-key",
|
||||||
|
"test-api-key",
|
||||||
|
"--filter-prefix",
|
||||||
|
"Tank01",
|
||||||
|
"--max-events",
|
||||||
|
"1",
|
||||||
|
],
|
||||||
|
output,
|
||||||
|
error,
|
||||||
|
_ => fakeClient);
|
||||||
|
|
||||||
|
Assert.Equal(0, exitCode);
|
||||||
|
StreamAlarmsRequest request = Assert.Single(fakeClient.StreamAlarmsRequests);
|
||||||
|
Assert.Equal("Tank01", request.AlarmFilterPrefix);
|
||||||
|
string text = output.ToString();
|
||||||
|
Assert.Contains("active-alarm", text);
|
||||||
|
Assert.Contains("Tank01.Level.HiHi", text);
|
||||||
|
Assert.DoesNotContain("snapshot-complete", text);
|
||||||
|
Assert.Equal(string.Empty, error.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Verifies that acknowledge-alarm builds a request and prints the JSON reply.</summary>
|
||||||
|
[Fact]
|
||||||
|
public async Task RunAsync_AcknowledgeAlarm_BuildsRequestAndPrintsJsonReply()
|
||||||
|
{
|
||||||
|
using var output = new StringWriter();
|
||||||
|
using var error = new StringWriter();
|
||||||
|
FakeCliClient fakeClient = new();
|
||||||
|
fakeClient.AcknowledgeAlarmReplies.Enqueue(new AcknowledgeAlarmReply
|
||||||
|
{
|
||||||
|
CorrelationId = "ack-fixture",
|
||||||
|
ProtocolStatus = new ProtocolStatus { Code = ProtocolStatusCode.Ok },
|
||||||
|
Hresult = 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
int exitCode = await MxGatewayClientCli.RunAsync(
|
||||||
|
[
|
||||||
|
"acknowledge-alarm",
|
||||||
|
"--endpoint",
|
||||||
|
"http://localhost:5000",
|
||||||
|
"--api-key",
|
||||||
|
"test-api-key",
|
||||||
|
"--reference",
|
||||||
|
"Tank01.Level.HiHi",
|
||||||
|
"--comment",
|
||||||
|
"ack from cli",
|
||||||
|
"--operator",
|
||||||
|
"operator1",
|
||||||
|
"--json",
|
||||||
|
],
|
||||||
|
output,
|
||||||
|
error,
|
||||||
|
_ => fakeClient);
|
||||||
|
|
||||||
|
Assert.Equal(0, exitCode);
|
||||||
|
AcknowledgeAlarmRequest request = Assert.Single(fakeClient.AcknowledgeAlarmRequests);
|
||||||
|
Assert.Equal("Tank01.Level.HiHi", request.AlarmFullReference);
|
||||||
|
Assert.Equal("ack from cli", request.Comment);
|
||||||
|
Assert.Equal("operator1", request.OperatorUser);
|
||||||
|
Assert.Contains("ack-fixture", output.ToString());
|
||||||
|
Assert.Equal(string.Empty, error.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Verifies that smoke command closes opened session when a command fails.</summary>
|
/// <summary>Verifies that smoke command closes opened session when a command fails.</summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task RunAsync_Smoke_WhenCommandFails_ClosesOpenedSession()
|
public async Task RunAsync_Smoke_WhenCommandFails_ClosesOpenedSession()
|
||||||
@@ -368,6 +449,66 @@ public sealed class MxGatewayClientCliTests
|
|||||||
Assert.Contains("\"objectCount\": 99", text);
|
Assert.Contains("\"objectCount\": 99", text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Verifies that batch mode dispatches a single version command and emits the EOR sentinel.</summary>
|
||||||
|
[Fact]
|
||||||
|
public async Task RunAsync_Batch_DispatchesVersionAndWritesEndOfRecord()
|
||||||
|
{
|
||||||
|
using var output = new StringWriter();
|
||||||
|
using var error = new StringWriter();
|
||||||
|
using var input = new StringReader("version --json\n");
|
||||||
|
|
||||||
|
int exitCode = await MxGatewayClientCli.RunAsync(
|
||||||
|
["batch"],
|
||||||
|
output,
|
||||||
|
error,
|
||||||
|
clientFactory: null,
|
||||||
|
standardInput: input);
|
||||||
|
|
||||||
|
Assert.Equal(0, exitCode);
|
||||||
|
string text = output.ToString();
|
||||||
|
Assert.Contains("\"gatewayProtocolVersion\":3", text);
|
||||||
|
Assert.Contains("__MXGW_BATCH_EOR__", text);
|
||||||
|
// The EOR marker must come after the JSON output.
|
||||||
|
int jsonIndex = text.IndexOf("\"gatewayProtocolVersion\"", StringComparison.Ordinal);
|
||||||
|
int eorIndex = text.IndexOf("__MXGW_BATCH_EOR__", StringComparison.Ordinal);
|
||||||
|
Assert.True(jsonIndex >= 0 && eorIndex > jsonIndex);
|
||||||
|
Assert.Equal(string.Empty, error.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Verifies that batch mode routes per-command errors to stdout as JSON between EOR markers.</summary>
|
||||||
|
[Fact]
|
||||||
|
public async Task RunAsync_Batch_WritesErrorsToStdoutAsJson()
|
||||||
|
{
|
||||||
|
using var output = new StringWriter();
|
||||||
|
using var error = new StringWriter();
|
||||||
|
// Unknown command should produce an error on the captured error stream,
|
||||||
|
// which batch mode re-emits to stdout inside the same delimited block.
|
||||||
|
using var input = new StringReader("nope-not-a-command\nversion\n");
|
||||||
|
|
||||||
|
int exitCode = await MxGatewayClientCli.RunAsync(
|
||||||
|
["batch"],
|
||||||
|
output,
|
||||||
|
error,
|
||||||
|
clientFactory: null,
|
||||||
|
standardInput: input);
|
||||||
|
|
||||||
|
Assert.Equal(0, exitCode);
|
||||||
|
string text = output.ToString();
|
||||||
|
// Two records → two EOR markers.
|
||||||
|
int firstEor = text.IndexOf("__MXGW_BATCH_EOR__", StringComparison.Ordinal);
|
||||||
|
int secondEor = text.IndexOf(
|
||||||
|
"__MXGW_BATCH_EOR__",
|
||||||
|
firstEor + 1,
|
||||||
|
StringComparison.Ordinal);
|
||||||
|
Assert.True(firstEor > 0);
|
||||||
|
Assert.True(secondEor > firstEor);
|
||||||
|
// The unknown-command error message must be on stdout (not on stderr).
|
||||||
|
Assert.Contains("nope-not-a-command", text);
|
||||||
|
Assert.DoesNotContain("nope-not-a-command", error.ToString());
|
||||||
|
// The follow-up `version` line should still succeed.
|
||||||
|
Assert.Contains("gateway-protocol=", text);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Fake CLI client for testing.</summary>
|
/// <summary>Fake CLI client for testing.</summary>
|
||||||
private sealed class FakeCliClient : IMxGatewayCliClient
|
private sealed class FakeCliClient : IMxGatewayCliClient
|
||||||
{
|
{
|
||||||
@@ -447,6 +588,41 @@ public sealed class MxGatewayClientCliTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Queue of acknowledge-alarm replies to return.</summary>
|
||||||
|
public Queue<AcknowledgeAlarmReply> AcknowledgeAlarmReplies { get; } = new();
|
||||||
|
|
||||||
|
/// <summary>List of received acknowledge-alarm requests.</summary>
|
||||||
|
public List<AcknowledgeAlarmRequest> AcknowledgeAlarmRequests { get; } = [];
|
||||||
|
|
||||||
|
/// <summary>List of received stream-alarms requests.</summary>
|
||||||
|
public List<StreamAlarmsRequest> StreamAlarmsRequests { get; } = [];
|
||||||
|
|
||||||
|
/// <summary>List of alarm feed messages to yield when streaming alarms.</summary>
|
||||||
|
public List<AlarmFeedMessage> AlarmFeedMessages { get; } = [];
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Task<AcknowledgeAlarmReply> AcknowledgeAlarmAsync(
|
||||||
|
AcknowledgeAlarmRequest request,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
AcknowledgeAlarmRequests.Add(request);
|
||||||
|
return Task.FromResult(AcknowledgeAlarmReplies.Dequeue());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async IAsyncEnumerable<AlarmFeedMessage> StreamAlarmsAsync(
|
||||||
|
StreamAlarmsRequest request,
|
||||||
|
[System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
StreamAlarmsRequests.Add(request);
|
||||||
|
foreach (AlarmFeedMessage feedMessage in AlarmFeedMessages)
|
||||||
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
await Task.Yield();
|
||||||
|
yield return feedMessage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Galaxy test connection reply to return.</summary>
|
/// <summary>Galaxy test connection reply to return.</summary>
|
||||||
public TestConnectionReply GalaxyTestConnectionReply { get; set; } = new() { Ok = true };
|
public TestConnectionReply GalaxyTestConnectionReply { get; set; } = new() { Ok = true };
|
||||||
|
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
using MxGateway.Contracts;
|
using ZB.MOM.WW.MxGateway.Contracts;
|
||||||
|
|
||||||
namespace MxGateway.Client.Tests;
|
namespace ZB.MOM.WW.MxGateway.Client.Tests;
|
||||||
|
|
||||||
public sealed class MxGatewayClientContractInfoTests
|
public sealed class MxGatewayClientContractInfoTests
|
||||||
{
|
{
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace MxGateway.Client.Tests;
|
namespace ZB.MOM.WW.MxGateway.Client.Tests;
|
||||||
|
|
||||||
public sealed class MxGatewayClientOptionsTests
|
public sealed class MxGatewayClientOptionsTests
|
||||||
{
|
{
|
||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
using MxGateway.Contracts.Proto;
|
using ZB.MOM.WW.MxGateway.Contracts.Proto;
|
||||||
using Grpc.Core;
|
using Grpc.Core;
|
||||||
|
|
||||||
namespace MxGateway.Client.Tests;
|
namespace ZB.MOM.WW.MxGateway.Client.Tests;
|
||||||
|
|
||||||
/// <summary>Tests for MxGatewaySession and client command behavior.</summary>
|
/// <summary>Tests for MxGatewaySession and client command behavior.</summary>
|
||||||
public sealed class MxGatewayClientSessionTests
|
public sealed class MxGatewayClientSessionTests
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace MxGateway.Client.Tests;
|
namespace ZB.MOM.WW.MxGateway.Client.Tests;
|
||||||
|
|
||||||
public sealed class MxGatewayGeneratedContractTests
|
public sealed class MxGatewayGeneratedContractTests
|
||||||
{
|
{
|
||||||
+3
-3
@@ -1,9 +1,9 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Google.Protobuf;
|
using Google.Protobuf;
|
||||||
using MxGateway.Client;
|
using ZB.MOM.WW.MxGateway.Client;
|
||||||
using MxGateway.Contracts.Proto;
|
using ZB.MOM.WW.MxGateway.Contracts.Proto;
|
||||||
|
|
||||||
namespace MxGateway.Client.Tests;
|
namespace ZB.MOM.WW.MxGateway.Client.Tests;
|
||||||
|
|
||||||
public sealed class MxStatusProxyExtensionsTests
|
public sealed class MxStatusProxyExtensionsTests
|
||||||
{
|
{
|
||||||
+3
-3
@@ -1,9 +1,9 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Google.Protobuf;
|
using Google.Protobuf;
|
||||||
using MxGateway.Client;
|
using ZB.MOM.WW.MxGateway.Client;
|
||||||
using MxGateway.Contracts.Proto;
|
using ZB.MOM.WW.MxGateway.Contracts.Proto;
|
||||||
|
|
||||||
namespace MxGateway.Client.Tests;
|
namespace ZB.MOM.WW.MxGateway.Client.Tests;
|
||||||
|
|
||||||
public sealed class MxValueExtensionsTests
|
public sealed class MxValueExtensionsTests
|
||||||
{
|
{
|
||||||
+2
-2
@@ -19,8 +19,8 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\MxGateway.Client\MxGateway.Client.csproj" />
|
<ProjectReference Include="..\ZB.MOM.WW.MxGateway.Client\ZB.MOM.WW.MxGateway.Client.csproj" />
|
||||||
<ProjectReference Include="..\MxGateway.Client.Cli\MxGateway.Client.Cli.csproj" />
|
<ProjectReference Include="..\ZB.MOM.WW.MxGateway.Client.Cli\ZB.MOM.WW.MxGateway.Client.Cli.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<Solution>
|
||||||
|
<Configurations>
|
||||||
|
<Platform Name="Any CPU" />
|
||||||
|
<Platform Name="x64" />
|
||||||
|
<Platform Name="x86" />
|
||||||
|
</Configurations>
|
||||||
|
<Project Path="../../src/ZB.MOM.WW.MxGateway.Contracts/ZB.MOM.WW.MxGateway.Contracts.csproj" />
|
||||||
|
<Project Path="ZB.MOM.WW.MxGateway.Client.Cli/ZB.MOM.WW.MxGateway.Client.Cli.csproj" />
|
||||||
|
<Project Path="ZB.MOM.WW.MxGateway.Client.Tests/ZB.MOM.WW.MxGateway.Client.Tests.csproj" />
|
||||||
|
<Project Path="ZB.MOM.WW.MxGateway.Client/ZB.MOM.WW.MxGateway.Client.csproj" />
|
||||||
|
</Solution>
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
namespace ZB.MOM.WW.MxGateway.Client;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Filters and shape options for <see cref="GalaxyRepositoryClient.DiscoverHierarchyAsync(DiscoverHierarchyOptions, System.Threading.CancellationToken)"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Hand-written ergonomic wrapper around the generated
|
||||||
|
/// <c>DiscoverHierarchyRequest</c>: lets callers express a Galaxy-browse
|
||||||
|
/// slice with .NET-friendly nullable scalars and collection initializers,
|
||||||
|
/// without touching the protobuf message's <c>oneof root</c> directly.
|
||||||
|
/// </remarks>
|
||||||
|
public sealed class DiscoverHierarchyOptions
|
||||||
|
{
|
||||||
|
/// <summary>Restrict to the subtree rooted at this Galaxy <c>gobject_id</c>.</summary>
|
||||||
|
public int? RootGobjectId { get; init; }
|
||||||
|
|
||||||
|
/// <summary>Restrict to the subtree rooted at the object with this tag name.</summary>
|
||||||
|
public string? RootTagName { get; init; }
|
||||||
|
|
||||||
|
/// <summary>Restrict to the subtree rooted at this <c>contained_name</c> path.</summary>
|
||||||
|
public string? RootContainedPath { get; init; }
|
||||||
|
|
||||||
|
/// <summary>Maximum traversal depth, measured from the chosen root.</summary>
|
||||||
|
public int? MaxDepth { get; init; }
|
||||||
|
|
||||||
|
/// <summary>Restrict to objects whose Galaxy category is in this set.</summary>
|
||||||
|
public IReadOnlyList<int> CategoryIds { get; init; } = [];
|
||||||
|
|
||||||
|
/// <summary>Restrict to objects whose template chain contains any of these tokens.</summary>
|
||||||
|
public IReadOnlyList<string> TemplateChainContains { get; init; } = [];
|
||||||
|
|
||||||
|
/// <summary>Optional glob-style filter on <c>tag_name</c>.</summary>
|
||||||
|
public string? TagNameGlob { get; init; }
|
||||||
|
|
||||||
|
/// <summary>Whether to populate each <c>GalaxyObject.Attributes</c>. Null leaves the server default.</summary>
|
||||||
|
public bool? IncludeAttributes { get; init; }
|
||||||
|
|
||||||
|
/// <summary>Restrict to objects that bear at least one alarm attribute.</summary>
|
||||||
|
public bool AlarmBearingOnly { get; init; }
|
||||||
|
|
||||||
|
/// <summary>Restrict to objects that have at least one historized attribute.</summary>
|
||||||
|
public bool HistorizedOnly { get; init; }
|
||||||
|
}
|
||||||
+2
-2
@@ -2,14 +2,14 @@ using Google.Protobuf.WellKnownTypes;
|
|||||||
using Grpc.Core;
|
using Grpc.Core;
|
||||||
using Grpc.Net.Client;
|
using Grpc.Net.Client;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using MxGateway.Contracts.Proto.Galaxy;
|
using ZB.MOM.WW.MxGateway.Contracts.Proto.Galaxy;
|
||||||
using Polly;
|
using Polly;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Security;
|
using System.Net.Security;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
|
||||||
namespace MxGateway.Client;
|
namespace ZB.MOM.WW.MxGateway.Client;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides the .NET client entry point for the public Galaxy Repository gRPC API.
|
/// Provides the .NET client entry point for the public Galaxy Repository gRPC API.
|
||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
using Grpc.Core;
|
using Grpc.Core;
|
||||||
using MxGateway.Contracts.Proto.Galaxy;
|
using ZB.MOM.WW.MxGateway.Contracts.Proto.Galaxy;
|
||||||
|
|
||||||
namespace MxGateway.Client;
|
namespace ZB.MOM.WW.MxGateway.Client;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// gRPC implementation of IGalaxyRepositoryClientTransport.
|
/// gRPC implementation of IGalaxyRepositoryClientTransport.
|
||||||
+44
-2
@@ -1,7 +1,7 @@
|
|||||||
using Grpc.Core;
|
using Grpc.Core;
|
||||||
using MxGateway.Contracts.Proto;
|
using ZB.MOM.WW.MxGateway.Contracts.Proto;
|
||||||
|
|
||||||
namespace MxGateway.Client;
|
namespace ZB.MOM.WW.MxGateway.Client;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// gRPC implementation of IMxGatewayClientTransport.
|
/// gRPC implementation of IMxGatewayClientTransport.
|
||||||
@@ -175,6 +175,48 @@ internal sealed class GrpcMxGatewayClientTransport(
|
|||||||
return QueryActiveAlarmsAsync(request, callOptions);
|
return QueryActiveAlarmsAsync(request, callOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async IAsyncEnumerable<AlarmFeedMessage> StreamAlarmsAsync(
|
||||||
|
StreamAlarmsRequest request,
|
||||||
|
CallOptions callOptions,
|
||||||
|
[System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
CancellationToken effectiveCancellationToken = cancellationToken.CanBeCanceled
|
||||||
|
? cancellationToken
|
||||||
|
: callOptions.CancellationToken;
|
||||||
|
|
||||||
|
using AsyncServerStreamingCall<AlarmFeedMessage> call = RawClient.StreamAlarms(request, callOptions);
|
||||||
|
|
||||||
|
IAsyncStreamReader<AlarmFeedMessage> responseStream = call.ResponseStream;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
AlarmFeedMessage? message;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!await responseStream.MoveNext(effectiveCancellationToken).ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
message = responseStream.Current;
|
||||||
|
}
|
||||||
|
catch (RpcException exception)
|
||||||
|
{
|
||||||
|
throw MapRpcException(exception, effectiveCancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
yield return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
IAsyncEnumerable<AlarmFeedMessage> IMxGatewayClientTransport.StreamAlarmsAsync(
|
||||||
|
StreamAlarmsRequest request,
|
||||||
|
CallOptions callOptions)
|
||||||
|
{
|
||||||
|
return StreamAlarmsAsync(request, callOptions);
|
||||||
|
}
|
||||||
|
|
||||||
private static Exception MapRpcException(
|
private static Exception MapRpcException(
|
||||||
RpcException exception,
|
RpcException exception,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
using Grpc.Core;
|
using Grpc.Core;
|
||||||
using MxGateway.Contracts.Proto.Galaxy;
|
using ZB.MOM.WW.MxGateway.Contracts.Proto.Galaxy;
|
||||||
|
|
||||||
namespace MxGateway.Client;
|
namespace ZB.MOM.WW.MxGateway.Client;
|
||||||
|
|
||||||
/// <summary>Transport layer for Galaxy Repository gRPC operations.</summary>
|
/// <summary>Transport layer for Galaxy Repository gRPC operations.</summary>
|
||||||
internal interface IGalaxyRepositoryClientTransport
|
internal interface IGalaxyRepositoryClientTransport
|
||||||
+13
-2
@@ -1,7 +1,7 @@
|
|||||||
using Grpc.Core;
|
using Grpc.Core;
|
||||||
using MxGateway.Contracts.Proto;
|
using ZB.MOM.WW.MxGateway.Contracts.Proto;
|
||||||
|
|
||||||
namespace MxGateway.Client;
|
namespace ZB.MOM.WW.MxGateway.Client;
|
||||||
|
|
||||||
internal interface IMxGatewayClientTransport
|
internal interface IMxGatewayClientTransport
|
||||||
{
|
{
|
||||||
@@ -75,4 +75,15 @@ internal interface IMxGatewayClientTransport
|
|||||||
IAsyncEnumerable<ActiveAlarmSnapshot> QueryActiveAlarmsAsync(
|
IAsyncEnumerable<ActiveAlarmSnapshot> QueryActiveAlarmsAsync(
|
||||||
QueryActiveAlarmsRequest request,
|
QueryActiveAlarmsRequest request,
|
||||||
CallOptions callOptions);
|
CallOptions callOptions);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attaches to the gateway's central alarm feed — the current active-alarm
|
||||||
|
/// snapshot followed by live transitions.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">The stream request, optionally scoped by alarm-reference prefix.</param>
|
||||||
|
/// <param name="callOptions">gRPC call options.</param>
|
||||||
|
/// <returns>An async enumerable of alarm feed messages.</returns>
|
||||||
|
IAsyncEnumerable<AlarmFeedMessage> StreamAlarmsAsync(
|
||||||
|
StreamAlarmsRequest request,
|
||||||
|
CallOptions callOptions);
|
||||||
}
|
}
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
using MxGateway.Contracts.Proto;
|
using ZB.MOM.WW.MxGateway.Contracts.Proto;
|
||||||
|
|
||||||
namespace MxGateway.Client;
|
namespace ZB.MOM.WW.MxGateway.Client;
|
||||||
|
|
||||||
/// <summary>Exception thrown when an MXAccess command fails with a non-zero HResult or failing status.</summary>
|
/// <summary>Exception thrown when an MXAccess command fails with a non-zero HResult or failing status.</summary>
|
||||||
public sealed class MxAccessException : MxGatewayCommandException
|
public sealed class MxAccessException : MxGatewayCommandException
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
using MxGateway.Contracts.Proto;
|
using ZB.MOM.WW.MxGateway.Contracts.Proto;
|
||||||
|
|
||||||
namespace MxGateway.Client;
|
namespace ZB.MOM.WW.MxGateway.Client;
|
||||||
|
|
||||||
/// <summary>Extension methods for checking MxCommandReply success conditions.</summary>
|
/// <summary>Extension methods for checking MxCommandReply success conditions.</summary>
|
||||||
public static class MxCommandReplyExtensions
|
public static class MxCommandReplyExtensions
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
using MxGateway.Contracts.Proto;
|
using ZB.MOM.WW.MxGateway.Contracts.Proto;
|
||||||
|
|
||||||
namespace MxGateway.Client;
|
namespace ZB.MOM.WW.MxGateway.Client;
|
||||||
|
|
||||||
/// <summary>Exception thrown when an API key is invalid, expired, or malformed.</summary>
|
/// <summary>Exception thrown when an API key is invalid, expired, or malformed.</summary>
|
||||||
public sealed class MxGatewayAuthenticationException : MxGatewayException
|
public sealed class MxGatewayAuthenticationException : MxGatewayException
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
using MxGateway.Contracts.Proto;
|
using ZB.MOM.WW.MxGateway.Contracts.Proto;
|
||||||
|
|
||||||
namespace MxGateway.Client;
|
namespace ZB.MOM.WW.MxGateway.Client;
|
||||||
|
|
||||||
/// <summary>Exception thrown when the API key lacks required scopes for an operation.</summary>
|
/// <summary>Exception thrown when the API key lacks required scopes for an operation.</summary>
|
||||||
public sealed class MxGatewayAuthorizationException : MxGatewayException
|
public sealed class MxGatewayAuthorizationException : MxGatewayException
|
||||||
+24
-2
@@ -1,13 +1,13 @@
|
|||||||
using Grpc.Core;
|
using Grpc.Core;
|
||||||
using Grpc.Net.Client;
|
using Grpc.Net.Client;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using MxGateway.Contracts.Proto;
|
using ZB.MOM.WW.MxGateway.Contracts.Proto;
|
||||||
using Polly;
|
using Polly;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Security;
|
using System.Net.Security;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
|
||||||
namespace MxGateway.Client;
|
namespace ZB.MOM.WW.MxGateway.Client;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides the .NET client entry point for the public MXAccess Gateway gRPC API.
|
/// Provides the .NET client entry point for the public MXAccess Gateway gRPC API.
|
||||||
@@ -224,6 +224,28 @@ public sealed class MxGatewayClient : IAsyncDisposable
|
|||||||
return _transport.QueryActiveAlarmsAsync(request, CreateStreamCallOptions(cancellationToken));
|
return _transport.QueryActiveAlarmsAsync(request, CreateStreamCallOptions(cancellationToken));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attaches to the gateway's central alarm feed. The stream opens with one
|
||||||
|
/// <see cref="AlarmFeedMessage"/> per currently-active alarm (the
|
||||||
|
/// ConditionRefresh snapshot), then a single <c>snapshot_complete</c>, then a
|
||||||
|
/// <c>transition</c> for every subsequent raise / acknowledge / clear. Served
|
||||||
|
/// by the gateway's always-on alarm monitor — no worker session is opened, so
|
||||||
|
/// any number of clients may attach. Optionally scoped by alarm-reference
|
||||||
|
/// prefix (<see cref="StreamAlarmsRequest.AlarmFilterPrefix"/>).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">The stream request, optionally scoped by alarm-reference prefix.</param>
|
||||||
|
/// <param name="cancellationToken">Cancellation token for the stream.</param>
|
||||||
|
/// <returns>An async enumerable of alarm feed messages.</returns>
|
||||||
|
public IAsyncEnumerable<AlarmFeedMessage> StreamAlarmsAsync(
|
||||||
|
StreamAlarmsRequest request,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(request);
|
||||||
|
ThrowIfDisposed();
|
||||||
|
|
||||||
|
return _transport.StreamAlarmsAsync(request, CreateStreamCallOptions(cancellationToken));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Disposes the client and releases all resources.
|
/// Disposes the client and releases all resources.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
using MxGateway.Contracts;
|
using ZB.MOM.WW.MxGateway.Contracts;
|
||||||
|
|
||||||
namespace MxGateway.Client;
|
namespace ZB.MOM.WW.MxGateway.Client;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Exposes the protocol versions compiled into this client package.
|
/// Exposes the protocol versions compiled into this client package.
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace MxGateway.Client;
|
namespace ZB.MOM.WW.MxGateway.Client;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Configures the gRPC channel used by the .NET MXAccess Gateway client.
|
/// Configures the gRPC channel used by the .NET MXAccess Gateway client.
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace MxGateway.Client;
|
namespace ZB.MOM.WW.MxGateway.Client;
|
||||||
|
|
||||||
/// <summary>Configuration for automatic retry behavior on transient gRPC call failures.</summary>
|
/// <summary>Configuration for automatic retry behavior on transient gRPC call failures.</summary>
|
||||||
public sealed class MxGatewayClientRetryOptions
|
public sealed class MxGatewayClientRetryOptions
|
||||||
+2
-2
@@ -1,10 +1,10 @@
|
|||||||
using Grpc.Core;
|
using Grpc.Core;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using MxGateway.Contracts.Proto;
|
using ZB.MOM.WW.MxGateway.Contracts.Proto;
|
||||||
using Polly;
|
using Polly;
|
||||||
using Polly.Retry;
|
using Polly.Retry;
|
||||||
|
|
||||||
namespace MxGateway.Client;
|
namespace ZB.MOM.WW.MxGateway.Client;
|
||||||
|
|
||||||
/// <summary>Factory and helpers for exponential-backoff retry policies on transient gRPC failures.</summary>
|
/// <summary>Factory and helpers for exponential-backoff retry policies on transient gRPC failures.</summary>
|
||||||
internal static class MxGatewayClientRetryPolicy
|
internal static class MxGatewayClientRetryPolicy
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
using MxGateway.Contracts.Proto;
|
using ZB.MOM.WW.MxGateway.Contracts.Proto;
|
||||||
|
|
||||||
namespace MxGateway.Client;
|
namespace ZB.MOM.WW.MxGateway.Client;
|
||||||
|
|
||||||
/// <summary>Exception thrown when a gateway command fails due to an unclassified protocol error.</summary>
|
/// <summary>Exception thrown when a gateway command fails due to an unclassified protocol error.</summary>
|
||||||
public class MxGatewayCommandException : MxGatewayException
|
public class MxGatewayCommandException : MxGatewayException
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
using MxGateway.Contracts.Proto;
|
using ZB.MOM.WW.MxGateway.Contracts.Proto;
|
||||||
|
|
||||||
namespace MxGateway.Client;
|
namespace ZB.MOM.WW.MxGateway.Client;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Exception thrown when a gateway RPC call fails or returns an error status.
|
/// Exception thrown when a gateway RPC call fails or returns an error status.
|
||||||
+167
-2
@@ -1,6 +1,6 @@
|
|||||||
using MxGateway.Contracts.Proto;
|
using ZB.MOM.WW.MxGateway.Contracts.Proto;
|
||||||
|
|
||||||
namespace MxGateway.Client;
|
namespace ZB.MOM.WW.MxGateway.Client;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents one gateway-backed MXAccess session.
|
/// Represents one gateway-backed MXAccess session.
|
||||||
@@ -502,6 +502,171 @@ public sealed class MxGatewaySession : IAsyncDisposable
|
|||||||
return reply.UnsubscribeBulk?.Results.ToArray() ?? [];
|
return reply.UnsubscribeBulk?.Results.ToArray() ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Bulk Write — sequential MXAccess Write per entry on the worker's STA.
|
||||||
|
/// Per-item failures appear as <see cref="BulkWriteResult"/> entries with
|
||||||
|
/// <c>WasSuccessful = false</c>; the call never throws on per-item errors.
|
||||||
|
/// Protocol-level failures still throw via EnsureProtocolSuccess.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="serverHandle">The ServerHandle from register.</param>
|
||||||
|
/// <param name="entries">Per-item write entries; each carries the item handle, value, and user id.</param>
|
||||||
|
/// <param name="cancellationToken">Cancellation token.</param>
|
||||||
|
/// <returns>One <see cref="BulkWriteResult"/> per requested entry, in request order.</returns>
|
||||||
|
public async Task<IReadOnlyList<BulkWriteResult>> WriteBulkAsync(
|
||||||
|
int serverHandle,
|
||||||
|
IReadOnlyList<WriteBulkEntry> entries,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(entries);
|
||||||
|
|
||||||
|
WriteBulkCommand command = new() { ServerHandle = serverHandle };
|
||||||
|
command.Entries.Add(entries);
|
||||||
|
|
||||||
|
MxCommandReply reply = await InvokeCommandAsync(
|
||||||
|
new MxCommand
|
||||||
|
{
|
||||||
|
Kind = MxCommandKind.WriteBulk,
|
||||||
|
WriteBulk = command,
|
||||||
|
},
|
||||||
|
cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
reply.EnsureProtocolSuccess().EnsureMxAccessSuccess();
|
||||||
|
return reply.WriteBulk?.Results.ToArray() ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Bulk Write2 — sequential MXAccess Write2 (timestamped) per entry.
|
||||||
|
/// Per-item failures appear as <see cref="BulkWriteResult"/> entries with
|
||||||
|
/// <c>WasSuccessful = false</c>; the call never throws on per-item errors.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="serverHandle">The ServerHandle from register.</param>
|
||||||
|
/// <param name="entries">Per-item write entries; each carries the item handle, value, timestamp, and user id.</param>
|
||||||
|
/// <param name="cancellationToken">Cancellation token.</param>
|
||||||
|
/// <returns>One <see cref="BulkWriteResult"/> per requested entry, in request order.</returns>
|
||||||
|
public async Task<IReadOnlyList<BulkWriteResult>> Write2BulkAsync(
|
||||||
|
int serverHandle,
|
||||||
|
IReadOnlyList<Write2BulkEntry> entries,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(entries);
|
||||||
|
|
||||||
|
Write2BulkCommand command = new() { ServerHandle = serverHandle };
|
||||||
|
command.Entries.Add(entries);
|
||||||
|
|
||||||
|
MxCommandReply reply = await InvokeCommandAsync(
|
||||||
|
new MxCommand
|
||||||
|
{
|
||||||
|
Kind = MxCommandKind.Write2Bulk,
|
||||||
|
Write2Bulk = command,
|
||||||
|
},
|
||||||
|
cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
reply.EnsureProtocolSuccess().EnsureMxAccessSuccess();
|
||||||
|
return reply.Write2Bulk?.Results.ToArray() ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Bulk WriteSecured — sequential MXAccess WriteSecured per entry.
|
||||||
|
/// Credential-sensitive values must never reach logs; the client mirrors
|
||||||
|
/// the single-item WriteSecured redaction contract.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="serverHandle">The ServerHandle from register.</param>
|
||||||
|
/// <param name="entries">Per-item write entries; each carries the item handle, value, current user id, and verifier user id.</param>
|
||||||
|
/// <param name="cancellationToken">Cancellation token.</param>
|
||||||
|
/// <returns>One <see cref="BulkWriteResult"/> per requested entry, in request order.</returns>
|
||||||
|
public async Task<IReadOnlyList<BulkWriteResult>> WriteSecuredBulkAsync(
|
||||||
|
int serverHandle,
|
||||||
|
IReadOnlyList<WriteSecuredBulkEntry> entries,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(entries);
|
||||||
|
|
||||||
|
WriteSecuredBulkCommand command = new() { ServerHandle = serverHandle };
|
||||||
|
command.Entries.Add(entries);
|
||||||
|
|
||||||
|
MxCommandReply reply = await InvokeCommandAsync(
|
||||||
|
new MxCommand
|
||||||
|
{
|
||||||
|
Kind = MxCommandKind.WriteSecuredBulk,
|
||||||
|
WriteSecuredBulk = command,
|
||||||
|
},
|
||||||
|
cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
reply.EnsureProtocolSuccess().EnsureMxAccessSuccess();
|
||||||
|
return reply.WriteSecuredBulk?.Results.ToArray() ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Bulk WriteSecured2 — sequential MXAccess WriteSecured2 (timestamped) per entry.
|
||||||
|
/// Same redaction rules as <see cref="WriteSecuredBulkAsync"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="serverHandle">The ServerHandle from register.</param>
|
||||||
|
/// <param name="entries">Per-item write entries; each carries the item handle, value, timestamp, current user id, and verifier user id.</param>
|
||||||
|
/// <param name="cancellationToken">Cancellation token.</param>
|
||||||
|
/// <returns>One <see cref="BulkWriteResult"/> per requested entry, in request order.</returns>
|
||||||
|
public async Task<IReadOnlyList<BulkWriteResult>> WriteSecured2BulkAsync(
|
||||||
|
int serverHandle,
|
||||||
|
IReadOnlyList<WriteSecured2BulkEntry> entries,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(entries);
|
||||||
|
|
||||||
|
WriteSecured2BulkCommand command = new() { ServerHandle = serverHandle };
|
||||||
|
command.Entries.Add(entries);
|
||||||
|
|
||||||
|
MxCommandReply reply = await InvokeCommandAsync(
|
||||||
|
new MxCommand
|
||||||
|
{
|
||||||
|
Kind = MxCommandKind.WriteSecured2Bulk,
|
||||||
|
WriteSecured2Bulk = command,
|
||||||
|
},
|
||||||
|
cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
reply.EnsureProtocolSuccess().EnsureMxAccessSuccess();
|
||||||
|
return reply.WriteSecured2Bulk?.Results.ToArray() ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Bulk Read — snapshot the current value for each requested tag.
|
||||||
|
/// Returns the cached OnDataChange value when the tag is already advised
|
||||||
|
/// (<c>WasCached = true</c>), otherwise the worker takes the full AddItem +
|
||||||
|
/// Advise + wait + UnAdvise + RemoveItem snapshot lifecycle. Per-tag
|
||||||
|
/// failures (timeout, invalid tag) appear as <see cref="BulkReadResult"/>
|
||||||
|
/// entries with <c>WasSuccessful = false</c>; the call never throws on
|
||||||
|
/// per-tag errors.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="serverHandle">The ServerHandle from register.</param>
|
||||||
|
/// <param name="tagAddresses">Tag addresses to read (one per result).</param>
|
||||||
|
/// <param name="timeout">Per-call timeout for the snapshot lifecycle path; <see cref="TimeSpan.Zero"/> uses the gateway default.</param>
|
||||||
|
/// <param name="cancellationToken">Cancellation token.</param>
|
||||||
|
/// <returns>One <see cref="BulkReadResult"/> per requested tag, in request order.</returns>
|
||||||
|
public async Task<IReadOnlyList<BulkReadResult>> ReadBulkAsync(
|
||||||
|
int serverHandle,
|
||||||
|
IReadOnlyList<string> tagAddresses,
|
||||||
|
TimeSpan timeout,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(tagAddresses);
|
||||||
|
|
||||||
|
ReadBulkCommand command = new()
|
||||||
|
{
|
||||||
|
ServerHandle = serverHandle,
|
||||||
|
TimeoutMs = timeout <= TimeSpan.Zero ? 0u : (uint)Math.Min(timeout.TotalMilliseconds, uint.MaxValue),
|
||||||
|
};
|
||||||
|
command.TagAddresses.Add(tagAddresses);
|
||||||
|
|
||||||
|
MxCommandReply reply = await InvokeCommandAsync(
|
||||||
|
new MxCommand
|
||||||
|
{
|
||||||
|
Kind = MxCommandKind.ReadBulk,
|
||||||
|
ReadBulk = command,
|
||||||
|
},
|
||||||
|
cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
reply.EnsureProtocolSuccess().EnsureMxAccessSuccess();
|
||||||
|
return reply.ReadBulk?.Results.ToArray() ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Writes a value to an item on the MXAccess server.
|
/// Writes a value to an item on the MXAccess server.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
using MxGateway.Contracts.Proto;
|
using ZB.MOM.WW.MxGateway.Contracts.Proto;
|
||||||
|
|
||||||
namespace MxGateway.Client;
|
namespace ZB.MOM.WW.MxGateway.Client;
|
||||||
|
|
||||||
/// <summary>Exception thrown when a session is not found, not ready, or invalid.</summary>
|
/// <summary>Exception thrown when a session is not found, not ready, or invalid.</summary>
|
||||||
public sealed class MxGatewaySessionException : MxGatewayException
|
public sealed class MxGatewaySessionException : MxGatewayException
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
using MxGateway.Contracts.Proto;
|
using ZB.MOM.WW.MxGateway.Contracts.Proto;
|
||||||
|
|
||||||
namespace MxGateway.Client;
|
namespace ZB.MOM.WW.MxGateway.Client;
|
||||||
|
|
||||||
/// <summary>Exception thrown when the worker process is unavailable or fails to process a command.</summary>
|
/// <summary>Exception thrown when the worker process is unavailable or fails to process a command.</summary>
|
||||||
public sealed class MxGatewayWorkerException : MxGatewayException
|
public sealed class MxGatewayWorkerException : MxGatewayException
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
using MxGateway.Contracts.Proto;
|
using ZB.MOM.WW.MxGateway.Contracts.Proto;
|
||||||
|
|
||||||
namespace MxGateway.Client;
|
namespace ZB.MOM.WW.MxGateway.Client;
|
||||||
|
|
||||||
/// <summary>Extension methods for MxStatusProxy values.</summary>
|
/// <summary>Extension methods for MxStatusProxy values.</summary>
|
||||||
public static class MxStatusProxyExtensions
|
public static class MxStatusProxyExtensions
|
||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
using Google.Protobuf;
|
using Google.Protobuf;
|
||||||
using Google.Protobuf.WellKnownTypes;
|
using Google.Protobuf.WellKnownTypes;
|
||||||
using MxGateway.Contracts.Proto;
|
using ZB.MOM.WW.MxGateway.Contracts.Proto;
|
||||||
|
|
||||||
namespace MxGateway.Client;
|
namespace ZB.MOM.WW.MxGateway.Client;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates and projects gateway MXAccess values without hiding the raw
|
/// Creates and projects gateway MXAccess values without hiding the raw
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
[assembly: InternalsVisibleTo("ZB.MOM.WW.MxGateway.Client.Tests")]
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\..\src\MxGateway.Contracts\MxGateway.Contracts.csproj" />
|
<ProjectReference Include="..\..\..\src\ZB.MOM.WW.MxGateway.Contracts\ZB.MOM.WW.MxGateway.Contracts.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -84,6 +84,13 @@ goroutine cleanup. Raw protobuf messages remain available through the
|
|||||||
`errors.As` for `GatewayError`, `CommandError`, and `MxAccessError`; command
|
`errors.As` for `GatewayError`, `CommandError`, and `MxAccessError`; command
|
||||||
errors preserve the raw reply.
|
errors preserve the raw reply.
|
||||||
|
|
||||||
|
For alarms, the package exposes `Client.QueryActiveAlarms` for one-shot
|
||||||
|
snapshots, `Client.StreamAlarms` for the server-streaming feed, and
|
||||||
|
`Client.AcknowledgeAlarm` to ack an alarm by full reference. The streaming
|
||||||
|
call returns a `StreamAlarmsClient`; cancel its context to terminate the
|
||||||
|
stream. All three pass straight through to the gateway's central alarm
|
||||||
|
monitor.
|
||||||
|
|
||||||
## Galaxy Repository browse
|
## Galaxy Repository browse
|
||||||
|
|
||||||
The `GalaxyRepository` service (proto package `galaxy_repository.v1`) is a
|
The `GalaxyRepository` service (proto package `galaxy_repository.v1`) is a
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
@@ -14,6 +15,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
@@ -89,10 +91,26 @@ func runWithIO(ctx context.Context, args []string, stdout, stderr io.Writer) err
|
|||||||
return runSubscribeBulk(ctx, args[1:], stdout, stderr)
|
return runSubscribeBulk(ctx, args[1:], stdout, stderr)
|
||||||
case "unsubscribe-bulk":
|
case "unsubscribe-bulk":
|
||||||
return runUnsubscribeBulk(ctx, args[1:], stdout, stderr)
|
return runUnsubscribeBulk(ctx, args[1:], stdout, stderr)
|
||||||
|
case "read-bulk":
|
||||||
|
return runReadBulk(ctx, args[1:], stdout, stderr)
|
||||||
|
case "write-bulk":
|
||||||
|
return runWriteBulk(ctx, args[1:], stdout, stderr)
|
||||||
|
case "write2-bulk":
|
||||||
|
return runWrite2Bulk(ctx, args[1:], stdout, stderr)
|
||||||
|
case "write-secured-bulk":
|
||||||
|
return runWriteSecuredBulk(ctx, args[1:], stdout, stderr)
|
||||||
|
case "write-secured2-bulk":
|
||||||
|
return runWriteSecured2Bulk(ctx, args[1:], stdout, stderr)
|
||||||
|
case "bench-read-bulk":
|
||||||
|
return runBenchReadBulk(ctx, args[1:], stdout, stderr)
|
||||||
case "write":
|
case "write":
|
||||||
return runWrite(ctx, args[1:], stdout, stderr)
|
return runWrite(ctx, args[1:], stdout, stderr)
|
||||||
case "stream-events":
|
case "stream-events":
|
||||||
return runStreamEvents(ctx, args[1:], stdout, stderr)
|
return runStreamEvents(ctx, args[1:], stdout, stderr)
|
||||||
|
case "stream-alarms":
|
||||||
|
return runStreamAlarms(ctx, args[1:], stdout, stderr)
|
||||||
|
case "acknowledge-alarm":
|
||||||
|
return runAcknowledgeAlarm(ctx, args[1:], stdout, stderr)
|
||||||
case "smoke":
|
case "smoke":
|
||||||
return runSmoke(ctx, args[1:], stdout, stderr)
|
return runSmoke(ctx, args[1:], stdout, stderr)
|
||||||
case "galaxy-test-connection":
|
case "galaxy-test-connection":
|
||||||
@@ -103,6 +121,8 @@ func runWithIO(ctx context.Context, args []string, stdout, stderr io.Writer) err
|
|||||||
return runGalaxyDiscover(ctx, args[1:], stdout, stderr)
|
return runGalaxyDiscover(ctx, args[1:], stdout, stderr)
|
||||||
case "galaxy-watch":
|
case "galaxy-watch":
|
||||||
return runGalaxyWatch(ctx, args[1:], stdout, stderr)
|
return runGalaxyWatch(ctx, args[1:], stdout, stderr)
|
||||||
|
case "batch":
|
||||||
|
return runBatch(ctx, os.Stdin, stdout, stderr)
|
||||||
default:
|
default:
|
||||||
writeUsage(stderr)
|
writeUsage(stderr)
|
||||||
return fmt.Errorf("unknown command %q", args[0])
|
return fmt.Errorf("unknown command %q", args[0])
|
||||||
@@ -337,11 +357,363 @@ func runUnsubscribeBulk(ctx context.Context, args []string, stdout, stderr io.Wr
|
|||||||
}
|
}
|
||||||
defer client.Close()
|
defer client.Close()
|
||||||
|
|
||||||
|
handles, err := parseInt32List(*itemHandles)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
session := mxgateway.NewSessionForID(client, *sessionID)
|
session := mxgateway.NewSessionForID(client, *sessionID)
|
||||||
results, err := session.UnsubscribeBulk(ctx, int32(*serverHandle), parseInt32List(*itemHandles))
|
results, err := session.UnsubscribeBulk(ctx, int32(*serverHandle), handles)
|
||||||
return writeBulkOutput(stdout, *jsonOutput, "unsubscribe-bulk", options, results, err)
|
return writeBulkOutput(stdout, *jsonOutput, "unsubscribe-bulk", options, results, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func runReadBulk(ctx context.Context, args []string, stdout, stderr io.Writer) error {
|
||||||
|
flags := flag.NewFlagSet("read-bulk", flag.ContinueOnError)
|
||||||
|
flags.SetOutput(stderr)
|
||||||
|
common := bindCommonFlags(flags)
|
||||||
|
jsonOutput := flags.Bool("json", false, "write JSON output")
|
||||||
|
sessionID := flags.String("session-id", "", "gateway session id")
|
||||||
|
serverHandle := flags.Int("server-handle", 0, "MXAccess server handle")
|
||||||
|
items := flags.String("items", "", "comma-separated tag addresses")
|
||||||
|
timeoutMs := flags.Int("timeout-ms", 0, "per-tag snapshot timeout in milliseconds (0 = worker default)")
|
||||||
|
|
||||||
|
if err := flags.Parse(args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if *sessionID == "" || *items == "" {
|
||||||
|
return errors.New("session-id and items are required")
|
||||||
|
}
|
||||||
|
|
||||||
|
client, options, err := dialForCommand(ctx, common)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
session := mxgateway.NewSessionForID(client, *sessionID)
|
||||||
|
results, err := session.ReadBulk(ctx, int32(*serverHandle), parseStringList(*items), time.Duration(*timeoutMs)*time.Millisecond)
|
||||||
|
return writeReadBulkOutput(stdout, *jsonOutput, "read-bulk", options, results, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runWriteBulk(ctx context.Context, args []string, stdout, stderr io.Writer) error {
|
||||||
|
return runWriteBulkVariant(ctx, args, stdout, stderr, "write-bulk", false, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runWrite2Bulk(ctx context.Context, args []string, stdout, stderr io.Writer) error {
|
||||||
|
return runWriteBulkVariant(ctx, args, stdout, stderr, "write2-bulk", true, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runWriteSecuredBulk(ctx context.Context, args []string, stdout, stderr io.Writer) error {
|
||||||
|
return runWriteBulkVariant(ctx, args, stdout, stderr, "write-secured-bulk", false, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runWriteSecured2Bulk(ctx context.Context, args []string, stdout, stderr io.Writer) error {
|
||||||
|
return runWriteBulkVariant(ctx, args, stdout, stderr, "write-secured2-bulk", true, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// runWriteBulkVariant shares the flag-parsing + entry-build skeleton across
|
||||||
|
// the four bulk-write families. withTimestamp adds a --timestamp-value flag;
|
||||||
|
// secured switches from --user-id to --current-user-id / --verifier-user-id.
|
||||||
|
func runWriteBulkVariant(ctx context.Context, args []string, stdout, stderr io.Writer, command string, withTimestamp bool, secured bool) error {
|
||||||
|
flags := flag.NewFlagSet(command, flag.ContinueOnError)
|
||||||
|
flags.SetOutput(stderr)
|
||||||
|
common := bindCommonFlags(flags)
|
||||||
|
jsonOutput := flags.Bool("json", false, "write JSON output")
|
||||||
|
sessionID := flags.String("session-id", "", "gateway session id")
|
||||||
|
serverHandle := flags.Int("server-handle", 0, "MXAccess server handle")
|
||||||
|
itemHandles := flags.String("item-handles", "", "comma-separated item handles")
|
||||||
|
valueType := flags.String("type", "string", "value type: bool, int32, int64, float, double, string")
|
||||||
|
values := flags.String("values", "", "comma-separated values (one per item handle)")
|
||||||
|
userID := flags.Int("user-id", 0, "MXAccess user id (Write/Write2 variants)")
|
||||||
|
currentUserID := flags.Int("current-user-id", 0, "MXAccess current user id (Secured variants)")
|
||||||
|
verifierUserID := flags.Int("verifier-user-id", 0, "MXAccess verifier user id (Secured variants)")
|
||||||
|
timestampValue := flags.String("timestamp-value", "", "RFC 3339 timestamp shared across all entries (Write2/WriteSecured2 variants)")
|
||||||
|
|
||||||
|
if err := flags.Parse(args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if *sessionID == "" || *itemHandles == "" || *values == "" {
|
||||||
|
return errors.New("session-id, item-handles, and values are required")
|
||||||
|
}
|
||||||
|
|
||||||
|
handles, err := parseInt32List(*itemHandles)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
valueTexts := parseStringList(*values)
|
||||||
|
if len(handles) != len(valueTexts) {
|
||||||
|
return fmt.Errorf("item-handles count (%d) does not match values count (%d)", len(handles), len(valueTexts))
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedValues := make([]*mxgateway.MxValue, len(handles))
|
||||||
|
for i, text := range valueTexts {
|
||||||
|
v, err := parseValue(*valueType, text)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("entry %d: %w", i, err)
|
||||||
|
}
|
||||||
|
parsedValues[i] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
var tsValue *mxgateway.MxValue
|
||||||
|
if withTimestamp {
|
||||||
|
if *timestampValue == "" {
|
||||||
|
return errors.New("timestamp-value is required for write2/write-secured2 bulk variants")
|
||||||
|
}
|
||||||
|
parsed, err := parseRfc3339Timestamp(*timestampValue)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tsValue = parsed
|
||||||
|
}
|
||||||
|
|
||||||
|
client, options, err := dialForCommand(ctx, common)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
session := mxgateway.NewSessionForID(client, *sessionID)
|
||||||
|
|
||||||
|
var results []*mxgateway.BulkWriteResult
|
||||||
|
switch command {
|
||||||
|
case "write-bulk":
|
||||||
|
entries := make([]*mxgateway.WriteBulkEntry, len(handles))
|
||||||
|
for i := range handles {
|
||||||
|
entries[i] = &mxgateway.WriteBulkEntry{ItemHandle: handles[i], Value: parsedValues[i], UserId: int32(*userID)}
|
||||||
|
}
|
||||||
|
results, err = session.WriteBulk(ctx, int32(*serverHandle), entries)
|
||||||
|
case "write2-bulk":
|
||||||
|
entries := make([]*mxgateway.Write2BulkEntry, len(handles))
|
||||||
|
for i := range handles {
|
||||||
|
entries[i] = &mxgateway.Write2BulkEntry{ItemHandle: handles[i], Value: parsedValues[i], TimestampValue: tsValue, UserId: int32(*userID)}
|
||||||
|
}
|
||||||
|
results, err = session.Write2Bulk(ctx, int32(*serverHandle), entries)
|
||||||
|
case "write-secured-bulk":
|
||||||
|
entries := make([]*mxgateway.WriteSecuredBulkEntry, len(handles))
|
||||||
|
for i := range handles {
|
||||||
|
entries[i] = &mxgateway.WriteSecuredBulkEntry{
|
||||||
|
ItemHandle: handles[i],
|
||||||
|
Value: parsedValues[i],
|
||||||
|
CurrentUserId: int32(*currentUserID),
|
||||||
|
VerifierUserId: int32(*verifierUserID),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
results, err = session.WriteSecuredBulk(ctx, int32(*serverHandle), entries)
|
||||||
|
case "write-secured2-bulk":
|
||||||
|
entries := make([]*mxgateway.WriteSecured2BulkEntry, len(handles))
|
||||||
|
for i := range handles {
|
||||||
|
entries[i] = &mxgateway.WriteSecured2BulkEntry{
|
||||||
|
ItemHandle: handles[i],
|
||||||
|
Value: parsedValues[i],
|
||||||
|
TimestampValue: tsValue,
|
||||||
|
CurrentUserId: int32(*currentUserID),
|
||||||
|
VerifierUserId: int32(*verifierUserID),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
results, err = session.WriteSecured2Bulk(ctx, int32(*serverHandle), entries)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported bulk write command %q", command)
|
||||||
|
}
|
||||||
|
_ = secured // currently only used for routing above; reserved for future per-variant validation
|
||||||
|
return writeWriteBulkOutput(stdout, *jsonOutput, command, options, results, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseRfc3339Timestamp parses an RFC 3339 timestamp and returns the
|
||||||
|
// MxValue protobuf representation used for the timestamped write families.
|
||||||
|
func parseRfc3339Timestamp(text string) (*mxgateway.MxValue, error) {
|
||||||
|
t, err := time.Parse(time.RFC3339Nano, text)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid RFC 3339 timestamp %q: %w", text, err)
|
||||||
|
}
|
||||||
|
return mxgateway.TimestampValue(t), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// runBenchReadBulk drives the cross-language ReadBulk stress benchmark from Go:
|
||||||
|
// opens its own session, subscribes to bulk-size tags so the worker value cache
|
||||||
|
// populates from real OnDataChange events, runs ReadBulk in a tight loop for
|
||||||
|
// duration-seconds with per-call timing, and emits the shared JSON schema the
|
||||||
|
// scripts/bench-read-bulk.ps1 driver collates across all five clients.
|
||||||
|
func runBenchReadBulk(ctx context.Context, args []string, stdout, stderr io.Writer) error {
|
||||||
|
flags := flag.NewFlagSet("bench-read-bulk", flag.ContinueOnError)
|
||||||
|
flags.SetOutput(stderr)
|
||||||
|
common := bindCommonFlags(flags)
|
||||||
|
jsonOutput := flags.Bool("json", false, "write JSON output")
|
||||||
|
clientName := flags.String("client-name", "mxgw-go-bench", "session client name")
|
||||||
|
durationSeconds := flags.Int("duration-seconds", 30, "steady-state measurement window in seconds")
|
||||||
|
warmupSeconds := flags.Int("warmup-seconds", 3, "warm-up window before measurement, in seconds")
|
||||||
|
bulkSize := flags.Int("bulk-size", 6, "tags per ReadBulk call")
|
||||||
|
tagStart := flags.Int("tag-start", 1, "first machine number")
|
||||||
|
tagPrefix := flags.String("tag-prefix", "TestMachine_", "tag prefix (machine number appended as %03d)")
|
||||||
|
tagAttribute := flags.String("tag-attribute", "TestChangingInt", "attribute appended to each tag prefix")
|
||||||
|
timeoutMs := flags.Int("timeout-ms", 1500, "per-tag snapshot timeout in milliseconds")
|
||||||
|
|
||||||
|
if err := flags.Parse(args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if *bulkSize < 1 {
|
||||||
|
return errors.New("bulk-size must be positive")
|
||||||
|
}
|
||||||
|
if *durationSeconds < 1 {
|
||||||
|
return errors.New("duration-seconds must be positive")
|
||||||
|
}
|
||||||
|
|
||||||
|
tags := make([]string, *bulkSize)
|
||||||
|
for i := 0; i < *bulkSize; i++ {
|
||||||
|
tags[i] = fmt.Sprintf("%s%03d.%s", *tagPrefix, *tagStart+i, *tagAttribute)
|
||||||
|
}
|
||||||
|
|
||||||
|
client, options, err := dialForCommand(ctx, common)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
session, err := client.OpenSession(ctx, mxgateway.OpenSessionOptions{ClientSessionName: *clientName})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_, _ = session.Close(context.Background())
|
||||||
|
}()
|
||||||
|
|
||||||
|
serverHandle, err := session.Register(ctx, *clientName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribeResults, err := session.SubscribeBulk(ctx, serverHandle, tags)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
itemHandles := make([]int32, 0, len(subscribeResults))
|
||||||
|
for _, result := range subscribeResults {
|
||||||
|
if result.GetWasSuccessful() {
|
||||||
|
itemHandles = append(itemHandles, result.GetItemHandle())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if len(itemHandles) > 0 {
|
||||||
|
_, _ = session.UnsubscribeBulk(context.Background(), serverHandle, itemHandles)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Warm-up: drive identical calls so any first-call JIT / connection-pool
|
||||||
|
// setup is amortised before the measurement window opens.
|
||||||
|
warmupDeadline := time.Now().Add(time.Duration(*warmupSeconds) * time.Second)
|
||||||
|
timeout := time.Duration(*timeoutMs) * time.Millisecond
|
||||||
|
for time.Now().Before(warmupDeadline) {
|
||||||
|
_, _ = session.ReadBulk(ctx, serverHandle, tags, timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Steady state: per-call latency captured via time.Now() deltas.
|
||||||
|
latenciesMs := make([]float64, 0, 65536)
|
||||||
|
var totalReadResults int64
|
||||||
|
var cachedReadResults int64
|
||||||
|
var successfulCalls, failedCalls int
|
||||||
|
steadyStart := time.Now()
|
||||||
|
steadyDeadline := steadyStart.Add(time.Duration(*durationSeconds) * time.Second)
|
||||||
|
|
||||||
|
for time.Now().Before(steadyDeadline) {
|
||||||
|
callStart := time.Now()
|
||||||
|
results, err := session.ReadBulk(ctx, serverHandle, tags, timeout)
|
||||||
|
elapsed := time.Since(callStart)
|
||||||
|
latenciesMs = append(latenciesMs, float64(elapsed.Nanoseconds())/1e6)
|
||||||
|
if err != nil {
|
||||||
|
failedCalls++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
successfulCalls++
|
||||||
|
for _, r := range results {
|
||||||
|
totalReadResults++
|
||||||
|
if r.GetWasCached() {
|
||||||
|
cachedReadResults++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
steadyElapsed := time.Since(steadyStart)
|
||||||
|
totalCalls := successfulCalls + failedCalls
|
||||||
|
|
||||||
|
callsPerSecond := 0.0
|
||||||
|
if steadyElapsed.Seconds() > 0 {
|
||||||
|
callsPerSecond = float64(totalCalls) / steadyElapsed.Seconds()
|
||||||
|
}
|
||||||
|
|
||||||
|
stats := map[string]any{
|
||||||
|
"language": "go",
|
||||||
|
"command": "bench-read-bulk",
|
||||||
|
"endpoint": options.Endpoint,
|
||||||
|
"clientName": *clientName,
|
||||||
|
"bulkSize": *bulkSize,
|
||||||
|
"durationSeconds": *durationSeconds,
|
||||||
|
"warmupSeconds": *warmupSeconds,
|
||||||
|
"durationMs": steadyElapsed.Milliseconds(),
|
||||||
|
"tags": tags,
|
||||||
|
"totalCalls": totalCalls,
|
||||||
|
"successfulCalls": successfulCalls,
|
||||||
|
"failedCalls": failedCalls,
|
||||||
|
"totalReadResults": totalReadResults,
|
||||||
|
"cachedReadResults": cachedReadResults,
|
||||||
|
"callsPerSecond": roundTo(callsPerSecond, 2),
|
||||||
|
"latencyMs": percentileSummary(latenciesMs),
|
||||||
|
}
|
||||||
|
if *jsonOutput {
|
||||||
|
return writeJSON(stdout, stats)
|
||||||
|
}
|
||||||
|
fmt.Fprintln(stdout, callsPerSecond)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// percentileSummary returns the same { p50, p95, p99, max, mean } shape every
|
||||||
|
// language bench emits, rounded to 3 decimal places so the PowerShell driver
|
||||||
|
// sees one schema across all five clients.
|
||||||
|
func percentileSummary(sample []float64) map[string]float64 {
|
||||||
|
if len(sample) == 0 {
|
||||||
|
return map[string]float64{"p50": 0, "p95": 0, "p99": 0, "max": 0, "mean": 0}
|
||||||
|
}
|
||||||
|
sorted := append([]float64(nil), sample...)
|
||||||
|
sort.Float64s(sorted)
|
||||||
|
mean := 0.0
|
||||||
|
maxValue := sorted[len(sorted)-1]
|
||||||
|
for _, v := range sample {
|
||||||
|
mean += v
|
||||||
|
}
|
||||||
|
mean /= float64(len(sample))
|
||||||
|
return map[string]float64{
|
||||||
|
"p50": roundTo(percentile(sorted, 0.50), 3),
|
||||||
|
"p95": roundTo(percentile(sorted, 0.95), 3),
|
||||||
|
"p99": roundTo(percentile(sorted, 0.99), 3),
|
||||||
|
"max": roundTo(maxValue, 3),
|
||||||
|
"mean": roundTo(mean, 3),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// percentile uses nearest-rank with linear interpolation; matches the .NET
|
||||||
|
// implementation so cross-language comparisons are apples-to-apples.
|
||||||
|
func percentile(sorted []float64, quantile float64) float64 {
|
||||||
|
if len(sorted) == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if len(sorted) == 1 {
|
||||||
|
return sorted[0]
|
||||||
|
}
|
||||||
|
rank := quantile * float64(len(sorted)-1)
|
||||||
|
lower := int(rank)
|
||||||
|
upper := lower + 1
|
||||||
|
if upper >= len(sorted) {
|
||||||
|
return sorted[lower]
|
||||||
|
}
|
||||||
|
fraction := rank - float64(lower)
|
||||||
|
return sorted[lower] + (sorted[upper]-sorted[lower])*fraction
|
||||||
|
}
|
||||||
|
|
||||||
|
func roundTo(value float64, digits int) float64 {
|
||||||
|
shift := 1.0
|
||||||
|
for i := 0; i < digits; i++ {
|
||||||
|
shift *= 10
|
||||||
|
}
|
||||||
|
return float64(int64(value*shift+0.5)) / shift
|
||||||
|
}
|
||||||
|
|
||||||
func runWrite(ctx context.Context, args []string, stdout, stderr io.Writer) error {
|
func runWrite(ctx context.Context, args []string, stdout, stderr io.Writer) error {
|
||||||
flags := flag.NewFlagSet("write", flag.ContinueOnError)
|
flags := flag.NewFlagSet("write", flag.ContinueOnError)
|
||||||
flags.SetOutput(stderr)
|
flags.SetOutput(stderr)
|
||||||
@@ -428,6 +800,119 @@ func runStreamEvents(ctx context.Context, args []string, stdout, stderr io.Write
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func runStreamAlarms(ctx context.Context, args []string, stdout, stderr io.Writer) error {
|
||||||
|
flags := flag.NewFlagSet("stream-alarms", flag.ContinueOnError)
|
||||||
|
flags.SetOutput(stderr)
|
||||||
|
common := bindCommonFlags(flags)
|
||||||
|
jsonOutput := flags.Bool("json", false, "write JSON output")
|
||||||
|
filterPrefix := flags.String("filter-prefix", "", "alarm-reference prefix scoping the feed; empty means unscoped")
|
||||||
|
limit := flags.Int("limit", 0, "maximum feed messages to read; 0 means unbounded")
|
||||||
|
|
||||||
|
if err := flags.Parse(args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
client, _, err := dialForCommand(ctx, common)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
// Mirror runStreamEvents so Ctrl+C on a long-running stream-alarms command
|
||||||
|
// cancels the gRPC stream cleanly (the gateway sees codes.Canceled rather
|
||||||
|
// than a torn TCP connection) and the deferred client.Close() actually runs.
|
||||||
|
signalCtx, stopSignals := signal.NotifyContext(ctx, os.Interrupt, syscall.SIGTERM)
|
||||||
|
defer stopSignals()
|
||||||
|
|
||||||
|
streamCtx, cancelStream := context.WithCancel(signalCtx)
|
||||||
|
defer cancelStream()
|
||||||
|
stream, err := client.StreamAlarms(streamCtx, &mxgateway.StreamAlarmsRequest{AlarmFilterPrefix: *filterPrefix})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
count := 0
|
||||||
|
for {
|
||||||
|
message, err := stream.Recv()
|
||||||
|
if errors.Is(err, io.EOF) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if *jsonOutput {
|
||||||
|
fmt.Fprintln(stdout, string(mustMarshalProto(message)))
|
||||||
|
} else {
|
||||||
|
fmt.Fprintln(stdout, formatAlarmFeedMessage(message))
|
||||||
|
}
|
||||||
|
count++
|
||||||
|
if *limit > 0 && count >= *limit {
|
||||||
|
cancelStream()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatAlarmFeedMessage renders one AlarmFeedMessage in the CLI's plain-text
|
||||||
|
// output style, distinguishing the active-alarm snapshot, snapshot-complete
|
||||||
|
// sentinel, and transition cases of the message's payload oneof.
|
||||||
|
func formatAlarmFeedMessage(message *mxgateway.AlarmFeedMessage) string {
|
||||||
|
switch {
|
||||||
|
case message.GetActiveAlarm() != nil:
|
||||||
|
alarm := message.GetActiveAlarm()
|
||||||
|
return fmt.Sprintf("active-alarm %s state=%s severity=%d", alarm.GetAlarmFullReference(), alarm.GetCurrentState(), alarm.GetSeverity())
|
||||||
|
case message.GetSnapshotComplete():
|
||||||
|
return "snapshot-complete"
|
||||||
|
case message.GetTransition() != nil:
|
||||||
|
transition := message.GetTransition()
|
||||||
|
return fmt.Sprintf("transition %s kind=%s severity=%d", transition.GetAlarmFullReference(), transition.GetTransitionKind(), transition.GetSeverity())
|
||||||
|
default:
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runAcknowledgeAlarm(ctx context.Context, args []string, stdout, stderr io.Writer) error {
|
||||||
|
flags := flag.NewFlagSet("acknowledge-alarm", flag.ContinueOnError)
|
||||||
|
flags.SetOutput(stderr)
|
||||||
|
common := bindCommonFlags(flags)
|
||||||
|
jsonOutput := flags.Bool("json", false, "write JSON output")
|
||||||
|
reference := flags.String("reference", "", "full alarm reference to acknowledge")
|
||||||
|
comment := flags.String("comment", "", "operator acknowledge comment")
|
||||||
|
operator := flags.String("operator", "", "operator user performing the acknowledge")
|
||||||
|
|
||||||
|
if err := flags.Parse(args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if *reference == "" {
|
||||||
|
return errors.New("reference is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
client, options, err := dialForCommand(ctx, common)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
reply, err := client.AcknowledgeAlarm(ctx, &mxgateway.AcknowledgeAlarmRequest{
|
||||||
|
AlarmFullReference: *reference,
|
||||||
|
Comment: *comment,
|
||||||
|
OperatorUser: *operator,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if *jsonOutput {
|
||||||
|
return writeJSON(stdout, commandReplyOutput{
|
||||||
|
Command: "acknowledge-alarm",
|
||||||
|
Options: options,
|
||||||
|
Reply: mustMarshalProto(reply),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintln(stdout, reply.GetHresult())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func runSmoke(ctx context.Context, args []string, stdout, stderr io.Writer) error {
|
func runSmoke(ctx context.Context, args []string, stdout, stderr io.Writer) error {
|
||||||
flags := flag.NewFlagSet("smoke", flag.ContinueOnError)
|
flags := flag.NewFlagSet("smoke", flag.ContinueOnError)
|
||||||
flags.SetOutput(stderr)
|
flags.SetOutput(stderr)
|
||||||
@@ -514,7 +999,7 @@ func parseStringList(value string) []string {
|
|||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseInt32List(value string) []int32 {
|
func parseInt32List(value string) ([]int32, error) {
|
||||||
parts := strings.Split(value, ",")
|
parts := strings.Split(value, ",")
|
||||||
items := make([]int32, 0, len(parts))
|
items := make([]int32, 0, len(parts))
|
||||||
for _, part := range parts {
|
for _, part := range parts {
|
||||||
@@ -524,11 +1009,11 @@ func parseInt32List(value string) []int32 {
|
|||||||
}
|
}
|
||||||
parsed, err := strconv.ParseInt(item, 10, 32)
|
parsed, err := strconv.ParseInt(item, 10, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return nil, fmt.Errorf("invalid item handle %q: %w", item, err)
|
||||||
}
|
}
|
||||||
items = append(items, int32(parsed))
|
items = append(items, int32(parsed))
|
||||||
}
|
}
|
||||||
return items
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func bindCommonFlags(flags *flag.FlagSet) *commonOptions {
|
func bindCommonFlags(flags *flag.FlagSet) *commonOptions {
|
||||||
@@ -647,6 +1132,36 @@ func writeBulkOutput(stdout io.Writer, jsonOutput bool, command string, options
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func writeWriteBulkOutput(stdout io.Writer, jsonOutput bool, command string, options commonOptions, results []*mxgateway.BulkWriteResult, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if jsonOutput {
|
||||||
|
return writeJSON(stdout, map[string]any{
|
||||||
|
"command": command,
|
||||||
|
"options": options,
|
||||||
|
"results": results,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fmt.Fprintln(stdout, len(results))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeReadBulkOutput(stdout io.Writer, jsonOutput bool, command string, options commonOptions, results []*mxgateway.BulkReadResult, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if jsonOutput {
|
||||||
|
return writeJSON(stdout, map[string]any{
|
||||||
|
"command": command,
|
||||||
|
"options": options,
|
||||||
|
"results": results,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fmt.Fprintln(stdout, len(results))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func writeJSON(writer io.Writer, value any) error {
|
func writeJSON(writer io.Writer, value any) error {
|
||||||
encoder := json.NewEncoder(writer)
|
encoder := json.NewEncoder(writer)
|
||||||
encoder.SetIndent("", " ")
|
encoder.SetIndent("", " ")
|
||||||
@@ -666,7 +1181,43 @@ type protojsonMessage interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func writeUsage(writer io.Writer) {
|
func writeUsage(writer io.Writer) {
|
||||||
fmt.Fprintln(writer, "usage: mxgw-go <version|open-session|close-session|register|add-item|advise|subscribe-bulk|unsubscribe-bulk|write|stream-events|smoke|galaxy-test-connection|galaxy-last-deploy|galaxy-discover|galaxy-watch>")
|
fmt.Fprintln(writer, "usage: mxgw-go <version|open-session|close-session|register|add-item|advise|subscribe-bulk|unsubscribe-bulk|read-bulk|write-bulk|write2-bulk|write-secured-bulk|write-secured2-bulk|bench-read-bulk|write|stream-events|stream-alarms|acknowledge-alarm|smoke|galaxy-test-connection|galaxy-last-deploy|galaxy-discover|galaxy-watch|batch>")
|
||||||
|
}
|
||||||
|
|
||||||
|
// batchEOR is the end-of-result sentinel emitted to stdout after every command
|
||||||
|
// in batch mode, regardless of success or failure.
|
||||||
|
const batchEOR = "__MXGW_BATCH_EOR__"
|
||||||
|
|
||||||
|
// runBatch reads one command line at a time from in, dispatches each via the
|
||||||
|
// normal runWithIO routing, and writes a batchEOR sentinel to stdout after
|
||||||
|
// every result. Errors are serialised as JSON to stdout (not stderr) so the
|
||||||
|
// harness can parse them without interleaving stderr. The loop never terminates
|
||||||
|
// on command error; only stdin EOF (or an empty line) ends the session.
|
||||||
|
func runBatch(ctx context.Context, in io.Reader, stdout, stderr io.Writer) error {
|
||||||
|
bw := bufio.NewWriter(stdout)
|
||||||
|
scanner := bufio.NewScanner(in)
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
if line == "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
args := strings.Fields(line)
|
||||||
|
if len(args) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := runWithIO(ctx, args, bw, stderr); err != nil {
|
||||||
|
// Write error as JSON to stdout (bw) so the harness sees it in the
|
||||||
|
// same stream as normal output, framed by the EOR sentinel.
|
||||||
|
errPayload := map[string]string{
|
||||||
|
"error": err.Error(),
|
||||||
|
"type": "error",
|
||||||
|
}
|
||||||
|
_ = writeJSON(bw, errPayload)
|
||||||
|
}
|
||||||
|
_, _ = fmt.Fprintln(bw, batchEOR)
|
||||||
|
_ = bw.Flush()
|
||||||
|
}
|
||||||
|
return scanner.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
func dialGalaxyForCommand(ctx context.Context, common *commonOptions) (*mxgateway.GalaxyClient, commonOptions, error) {
|
func dialGalaxyForCommand(ctx context.Context, common *commonOptions) (*mxgateway.GalaxyClient, commonOptions, error) {
|
||||||
|
|||||||
@@ -47,6 +47,34 @@ func TestCommonOptionsRedactsAPIKey(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRunBatchEmitsEORAfterVersion(t *testing.T) {
|
||||||
|
var stdout bytes.Buffer
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
|
||||||
|
in := strings.NewReader("version --json\n")
|
||||||
|
if err := runBatch(t.Context(), in, &stdout, &stderr); err != nil {
|
||||||
|
t.Fatalf("runBatch() error = %v; stderr = %s", err, stderr.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
out := stdout.String()
|
||||||
|
if !strings.Contains(out, "\n"+batchEOR+"\n") && !strings.HasSuffix(out, batchEOR+"\n") {
|
||||||
|
t.Fatalf("expected EOR marker %q in stdout; got: %q", batchEOR, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
idx := strings.Index(out, batchEOR)
|
||||||
|
if idx <= 0 {
|
||||||
|
t.Fatalf("EOR marker not found or appeared before any output: %q", out)
|
||||||
|
}
|
||||||
|
payload := out[:idx]
|
||||||
|
var output versionOutput
|
||||||
|
if err := json.Unmarshal([]byte(payload), &output); err != nil {
|
||||||
|
t.Fatalf("parse JSON block before EOR: %v (payload=%q)", err, payload)
|
||||||
|
}
|
||||||
|
if output.GatewayProtocolVersion == 0 || output.WorkerProtocolVersion == 0 {
|
||||||
|
t.Fatalf("protocol versions were not populated: %+v", output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestParseValueBuildsTypedValue(t *testing.T) {
|
func TestParseValueBuildsTypedValue(t *testing.T) {
|
||||||
value, err := parseValue("int32", "123")
|
value, err := parseValue("int32", "123")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ Set-StrictMode -Version Latest
|
|||||||
$ErrorActionPreference = 'Stop'
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
$repoRoot = Resolve-Path (Join-Path $PSScriptRoot '..\..')
|
$repoRoot = Resolve-Path (Join-Path $PSScriptRoot '..\..')
|
||||||
$protoRoot = Join-Path $repoRoot 'src\MxGateway.Contracts\Protos'
|
$protoRoot = Join-Path $repoRoot 'src\ZB.MOM.WW.MxGateway.Contracts\Protos'
|
||||||
$outputRoot = Join-Path $PSScriptRoot 'internal\generated'
|
$outputRoot = Join-Path $PSScriptRoot 'internal\generated'
|
||||||
$modulePath = 'gitea.dohertylan.com/dohertj2/mxaccessgw/clients/go/internal/generated'
|
$modulePath = 'gitea.dohertylan.com/dohertj2/mxaccessgw/clients/go/internal/generated'
|
||||||
$protoc = 'C:\Users\dohertj2\AppData\Local\Microsoft\WinGet\Packages\Google.Protobuf_Microsoft.Winget.Source_8wekyb3d8bbwe\bin\protoc.exe'
|
$protoc = 'C:\Users\dohertj2\AppData\Local\Microsoft\WinGet\Packages\Google.Protobuf_Microsoft.Winget.Source_8wekyb3d8bbwe\bin\protoc.exe'
|
||||||
|
|||||||
@@ -687,18 +687,32 @@ func (x *GalaxyObject) GetAttributes() []*GalaxyAttribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type GalaxyAttribute struct {
|
type GalaxyAttribute struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
AttributeName string `protobuf:"bytes,1,opt,name=attribute_name,json=attributeName,proto3" json:"attribute_name,omitempty"`
|
AttributeName string `protobuf:"bytes,1,opt,name=attribute_name,json=attributeName,proto3" json:"attribute_name,omitempty"`
|
||||||
FullTagReference string `protobuf:"bytes,2,opt,name=full_tag_reference,json=fullTagReference,proto3" json:"full_tag_reference,omitempty"`
|
FullTagReference string `protobuf:"bytes,2,opt,name=full_tag_reference,json=fullTagReference,proto3" json:"full_tag_reference,omitempty"`
|
||||||
MxDataType int32 `protobuf:"varint,3,opt,name=mx_data_type,json=mxDataType,proto3" json:"mx_data_type,omitempty"`
|
// Raw Galaxy SQL `dbo.data_type` identifier, passed through unchanged.
|
||||||
DataTypeName string `protobuf:"bytes,4,opt,name=data_type_name,json=dataTypeName,proto3" json:"data_type_name,omitempty"`
|
// This is NOT a member of `mxaccess_gateway.v1.MxDataType` — Galaxy's
|
||||||
IsArray bool `protobuf:"varint,5,opt,name=is_array,json=isArray,proto3" json:"is_array,omitempty"`
|
// type enumeration is distinct from MXAccess's wire data-type enum and
|
||||||
ArrayDimension int32 `protobuf:"varint,6,opt,name=array_dimension,json=arrayDimension,proto3" json:"array_dimension,omitempty"`
|
// the two must not be cast or compared. The GalaxyRepository service is
|
||||||
ArrayDimensionPresent bool `protobuf:"varint,7,opt,name=array_dimension_present,json=arrayDimensionPresent,proto3" json:"array_dimension_present,omitempty"`
|
// metadata-only and deliberately does not share types with
|
||||||
MxAttributeCategory int32 `protobuf:"varint,8,opt,name=mx_attribute_category,json=mxAttributeCategory,proto3" json:"mx_attribute_category,omitempty"`
|
// mxaccess_gateway.proto. See docs/GalaxyRepository.md.
|
||||||
SecurityClassification int32 `protobuf:"varint,9,opt,name=security_classification,json=securityClassification,proto3" json:"security_classification,omitempty"`
|
MxDataType int32 `protobuf:"varint,3,opt,name=mx_data_type,json=mxDataType,proto3" json:"mx_data_type,omitempty"`
|
||||||
IsHistorized bool `protobuf:"varint,10,opt,name=is_historized,json=isHistorized,proto3" json:"is_historized,omitempty"`
|
// Human-readable name from Galaxy's `dbo.data_type` table (e.g. "Float",
|
||||||
IsAlarm bool `protobuf:"varint,11,opt,name=is_alarm,json=isAlarm,proto3" json:"is_alarm,omitempty"`
|
// "Integer", "Boolean"). Free-form Galaxy text; not a stable enum.
|
||||||
|
DataTypeName string `protobuf:"bytes,4,opt,name=data_type_name,json=dataTypeName,proto3" json:"data_type_name,omitempty"`
|
||||||
|
IsArray bool `protobuf:"varint,5,opt,name=is_array,json=isArray,proto3" json:"is_array,omitempty"`
|
||||||
|
ArrayDimension int32 `protobuf:"varint,6,opt,name=array_dimension,json=arrayDimension,proto3" json:"array_dimension,omitempty"`
|
||||||
|
ArrayDimensionPresent bool `protobuf:"varint,7,opt,name=array_dimension_present,json=arrayDimensionPresent,proto3" json:"array_dimension_present,omitempty"`
|
||||||
|
// Raw Galaxy SQL attribute-category identifier, passed through unchanged.
|
||||||
|
// Galaxy-specific; not mapped to any gateway enum. See
|
||||||
|
// docs/GalaxyRepository.md.
|
||||||
|
MxAttributeCategory int32 `protobuf:"varint,8,opt,name=mx_attribute_category,json=mxAttributeCategory,proto3" json:"mx_attribute_category,omitempty"`
|
||||||
|
// Raw Galaxy SQL security-classification identifier, passed through
|
||||||
|
// unchanged. Galaxy-specific; not mapped to any gateway enum. See
|
||||||
|
// docs/GalaxyRepository.md.
|
||||||
|
SecurityClassification int32 `protobuf:"varint,9,opt,name=security_classification,json=securityClassification,proto3" json:"security_classification,omitempty"`
|
||||||
|
IsHistorized bool `protobuf:"varint,10,opt,name=is_historized,json=isHistorized,proto3" json:"is_historized,omitempty"`
|
||||||
|
IsAlarm bool `protobuf:"varint,11,opt,name=is_alarm,json=isAlarm,proto3" json:"is_alarm,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@@ -888,7 +902,7 @@ const file_galaxy_repository_proto_rawDesc = "" +
|
|||||||
"\x0eTestConnection\x12+.galaxy_repository.v1.TestConnectionRequest\x1a).galaxy_repository.v1.TestConnectionReply\x12q\n" +
|
"\x0eTestConnection\x12+.galaxy_repository.v1.TestConnectionRequest\x1a).galaxy_repository.v1.TestConnectionReply\x12q\n" +
|
||||||
"\x11GetLastDeployTime\x12..galaxy_repository.v1.GetLastDeployTimeRequest\x1a,.galaxy_repository.v1.GetLastDeployTimeReply\x12q\n" +
|
"\x11GetLastDeployTime\x12..galaxy_repository.v1.GetLastDeployTimeRequest\x1a,.galaxy_repository.v1.GetLastDeployTimeReply\x12q\n" +
|
||||||
"\x11DiscoverHierarchy\x12..galaxy_repository.v1.DiscoverHierarchyRequest\x1a,.galaxy_repository.v1.DiscoverHierarchyReply\x12h\n" +
|
"\x11DiscoverHierarchy\x12..galaxy_repository.v1.DiscoverHierarchyRequest\x1a,.galaxy_repository.v1.DiscoverHierarchyReply\x12h\n" +
|
||||||
"\x11WatchDeployEvents\x12..galaxy_repository.v1.WatchDeployEventsRequest\x1a!.galaxy_repository.v1.DeployEvent0\x01B#\xaa\x02 MxGateway.Contracts.Proto.Galaxyb\x06proto3"
|
"\x11WatchDeployEvents\x12..galaxy_repository.v1.WatchDeployEventsRequest\x1a!.galaxy_repository.v1.DeployEvent0\x01B-\xaa\x02*ZB.MOM.WW.MxGateway.Contracts.Proto.Galaxyb\x06proto3"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
file_galaxy_repository_proto_rawDescOnce sync.Once
|
file_galaxy_repository_proto_rawDescOnce sync.Once
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -24,6 +24,7 @@ const (
|
|||||||
MxAccessGateway_Invoke_FullMethodName = "/mxaccess_gateway.v1.MxAccessGateway/Invoke"
|
MxAccessGateway_Invoke_FullMethodName = "/mxaccess_gateway.v1.MxAccessGateway/Invoke"
|
||||||
MxAccessGateway_StreamEvents_FullMethodName = "/mxaccess_gateway.v1.MxAccessGateway/StreamEvents"
|
MxAccessGateway_StreamEvents_FullMethodName = "/mxaccess_gateway.v1.MxAccessGateway/StreamEvents"
|
||||||
MxAccessGateway_AcknowledgeAlarm_FullMethodName = "/mxaccess_gateway.v1.MxAccessGateway/AcknowledgeAlarm"
|
MxAccessGateway_AcknowledgeAlarm_FullMethodName = "/mxaccess_gateway.v1.MxAccessGateway/AcknowledgeAlarm"
|
||||||
|
MxAccessGateway_StreamAlarms_FullMethodName = "/mxaccess_gateway.v1.MxAccessGateway/StreamAlarms"
|
||||||
MxAccessGateway_QueryActiveAlarms_FullMethodName = "/mxaccess_gateway.v1.MxAccessGateway/QueryActiveAlarms"
|
MxAccessGateway_QueryActiveAlarms_FullMethodName = "/mxaccess_gateway.v1.MxAccessGateway/QueryActiveAlarms"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -38,6 +39,17 @@ type MxAccessGatewayClient interface {
|
|||||||
Invoke(ctx context.Context, in *MxCommandRequest, opts ...grpc.CallOption) (*MxCommandReply, error)
|
Invoke(ctx context.Context, in *MxCommandRequest, opts ...grpc.CallOption) (*MxCommandReply, error)
|
||||||
StreamEvents(ctx context.Context, in *StreamEventsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[MxEvent], error)
|
StreamEvents(ctx context.Context, in *StreamEventsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[MxEvent], error)
|
||||||
AcknowledgeAlarm(ctx context.Context, in *AcknowledgeAlarmRequest, opts ...grpc.CallOption) (*AcknowledgeAlarmReply, error)
|
AcknowledgeAlarm(ctx context.Context, in *AcknowledgeAlarmRequest, opts ...grpc.CallOption) (*AcknowledgeAlarmReply, error)
|
||||||
|
// Session-less central alarm feed. The stream opens with the current
|
||||||
|
// active-alarm snapshot (one `active_alarm` per alarm), then a single
|
||||||
|
// `snapshot_complete`, then a `transition` for every subsequent change.
|
||||||
|
// Served by the gateway's always-on alarm monitor; any number of clients
|
||||||
|
// fan out from the single monitor without opening a worker session.
|
||||||
|
StreamAlarms(ctx context.Context, in *StreamAlarmsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[AlarmFeedMessage], error)
|
||||||
|
// Point-in-time snapshot of the currently-active alarm set served from the
|
||||||
|
// gateway's always-on alarm monitor cache (session-less). Used after a
|
||||||
|
// reconnect to seed Part 9 client state, or to reconcile alarms that may
|
||||||
|
// have been missed during a transport blip. Streamed so callers can
|
||||||
|
// begin processing without buffering the full set.
|
||||||
QueryActiveAlarms(ctx context.Context, in *QueryActiveAlarmsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ActiveAlarmSnapshot], error)
|
QueryActiveAlarms(ctx context.Context, in *QueryActiveAlarmsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ActiveAlarmSnapshot], error)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,9 +120,28 @@ func (c *mxAccessGatewayClient) AcknowledgeAlarm(ctx context.Context, in *Acknow
|
|||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *mxAccessGatewayClient) StreamAlarms(ctx context.Context, in *StreamAlarmsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[AlarmFeedMessage], error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
stream, err := c.cc.NewStream(ctx, &MxAccessGateway_ServiceDesc.Streams[1], MxAccessGateway_StreamAlarms_FullMethodName, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := &grpc.GenericClientStream[StreamAlarmsRequest, AlarmFeedMessage]{ClientStream: stream}
|
||||||
|
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := x.ClientStream.CloseSend(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type MxAccessGateway_StreamAlarmsClient = grpc.ServerStreamingClient[AlarmFeedMessage]
|
||||||
|
|
||||||
func (c *mxAccessGatewayClient) QueryActiveAlarms(ctx context.Context, in *QueryActiveAlarmsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ActiveAlarmSnapshot], error) {
|
func (c *mxAccessGatewayClient) QueryActiveAlarms(ctx context.Context, in *QueryActiveAlarmsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ActiveAlarmSnapshot], error) {
|
||||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
stream, err := c.cc.NewStream(ctx, &MxAccessGateway_ServiceDesc.Streams[1], MxAccessGateway_QueryActiveAlarms_FullMethodName, cOpts...)
|
stream, err := c.cc.NewStream(ctx, &MxAccessGateway_ServiceDesc.Streams[2], MxAccessGateway_QueryActiveAlarms_FullMethodName, cOpts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -138,6 +169,17 @@ type MxAccessGatewayServer interface {
|
|||||||
Invoke(context.Context, *MxCommandRequest) (*MxCommandReply, error)
|
Invoke(context.Context, *MxCommandRequest) (*MxCommandReply, error)
|
||||||
StreamEvents(*StreamEventsRequest, grpc.ServerStreamingServer[MxEvent]) error
|
StreamEvents(*StreamEventsRequest, grpc.ServerStreamingServer[MxEvent]) error
|
||||||
AcknowledgeAlarm(context.Context, *AcknowledgeAlarmRequest) (*AcknowledgeAlarmReply, error)
|
AcknowledgeAlarm(context.Context, *AcknowledgeAlarmRequest) (*AcknowledgeAlarmReply, error)
|
||||||
|
// Session-less central alarm feed. The stream opens with the current
|
||||||
|
// active-alarm snapshot (one `active_alarm` per alarm), then a single
|
||||||
|
// `snapshot_complete`, then a `transition` for every subsequent change.
|
||||||
|
// Served by the gateway's always-on alarm monitor; any number of clients
|
||||||
|
// fan out from the single monitor without opening a worker session.
|
||||||
|
StreamAlarms(*StreamAlarmsRequest, grpc.ServerStreamingServer[AlarmFeedMessage]) error
|
||||||
|
// Point-in-time snapshot of the currently-active alarm set served from the
|
||||||
|
// gateway's always-on alarm monitor cache (session-less). Used after a
|
||||||
|
// reconnect to seed Part 9 client state, or to reconcile alarms that may
|
||||||
|
// have been missed during a transport blip. Streamed so callers can
|
||||||
|
// begin processing without buffering the full set.
|
||||||
QueryActiveAlarms(*QueryActiveAlarmsRequest, grpc.ServerStreamingServer[ActiveAlarmSnapshot]) error
|
QueryActiveAlarms(*QueryActiveAlarmsRequest, grpc.ServerStreamingServer[ActiveAlarmSnapshot]) error
|
||||||
mustEmbedUnimplementedMxAccessGatewayServer()
|
mustEmbedUnimplementedMxAccessGatewayServer()
|
||||||
}
|
}
|
||||||
@@ -164,6 +206,9 @@ func (UnimplementedMxAccessGatewayServer) StreamEvents(*StreamEventsRequest, grp
|
|||||||
func (UnimplementedMxAccessGatewayServer) AcknowledgeAlarm(context.Context, *AcknowledgeAlarmRequest) (*AcknowledgeAlarmReply, error) {
|
func (UnimplementedMxAccessGatewayServer) AcknowledgeAlarm(context.Context, *AcknowledgeAlarmRequest) (*AcknowledgeAlarmReply, error) {
|
||||||
return nil, status.Error(codes.Unimplemented, "method AcknowledgeAlarm not implemented")
|
return nil, status.Error(codes.Unimplemented, "method AcknowledgeAlarm not implemented")
|
||||||
}
|
}
|
||||||
|
func (UnimplementedMxAccessGatewayServer) StreamAlarms(*StreamAlarmsRequest, grpc.ServerStreamingServer[AlarmFeedMessage]) error {
|
||||||
|
return status.Error(codes.Unimplemented, "method StreamAlarms not implemented")
|
||||||
|
}
|
||||||
func (UnimplementedMxAccessGatewayServer) QueryActiveAlarms(*QueryActiveAlarmsRequest, grpc.ServerStreamingServer[ActiveAlarmSnapshot]) error {
|
func (UnimplementedMxAccessGatewayServer) QueryActiveAlarms(*QueryActiveAlarmsRequest, grpc.ServerStreamingServer[ActiveAlarmSnapshot]) error {
|
||||||
return status.Error(codes.Unimplemented, "method QueryActiveAlarms not implemented")
|
return status.Error(codes.Unimplemented, "method QueryActiveAlarms not implemented")
|
||||||
}
|
}
|
||||||
@@ -271,6 +316,17 @@ func _MxAccessGateway_AcknowledgeAlarm_Handler(srv interface{}, ctx context.Cont
|
|||||||
return interceptor(ctx, in, info, handler)
|
return interceptor(ctx, in, info, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func _MxAccessGateway_StreamAlarms_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||||
|
m := new(StreamAlarmsRequest)
|
||||||
|
if err := stream.RecvMsg(m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return srv.(MxAccessGatewayServer).StreamAlarms(m, &grpc.GenericServerStream[StreamAlarmsRequest, AlarmFeedMessage]{ServerStream: stream})
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type MxAccessGateway_StreamAlarmsServer = grpc.ServerStreamingServer[AlarmFeedMessage]
|
||||||
|
|
||||||
func _MxAccessGateway_QueryActiveAlarms_Handler(srv interface{}, stream grpc.ServerStream) error {
|
func _MxAccessGateway_QueryActiveAlarms_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||||
m := new(QueryActiveAlarmsRequest)
|
m := new(QueryActiveAlarmsRequest)
|
||||||
if err := stream.RecvMsg(m); err != nil {
|
if err := stream.RecvMsg(m); err != nil {
|
||||||
@@ -312,6 +368,11 @@ var MxAccessGateway_ServiceDesc = grpc.ServiceDesc{
|
|||||||
Handler: _MxAccessGateway_StreamEvents_Handler,
|
Handler: _MxAccessGateway_StreamEvents_Handler,
|
||||||
ServerStreams: true,
|
ServerStreams: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
StreamName: "StreamAlarms",
|
||||||
|
Handler: _MxAccessGateway_StreamAlarms_Handler,
|
||||||
|
ServerStreams: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
StreamName: "QueryActiveAlarms",
|
StreamName: "QueryActiveAlarms",
|
||||||
Handler: _MxAccessGateway_QueryActiveAlarms_Handler,
|
Handler: _MxAccessGateway_QueryActiveAlarms_Handler,
|
||||||
|
|||||||
@@ -1179,7 +1179,7 @@ const file_mxaccess_worker_proto_rawDesc = "" +
|
|||||||
"\x1eWORKER_FAULT_CATEGORY_STA_HUNG\x10\t\x12(\n" +
|
"\x1eWORKER_FAULT_CATEGORY_STA_HUNG\x10\t\x12(\n" +
|
||||||
"$WORKER_FAULT_CATEGORY_QUEUE_OVERFLOW\x10\n" +
|
"$WORKER_FAULT_CATEGORY_QUEUE_OVERFLOW\x10\n" +
|
||||||
"\x12*\n" +
|
"\x12*\n" +
|
||||||
"&WORKER_FAULT_CATEGORY_SHUTDOWN_TIMEOUT\x10\vB\x1c\xaa\x02\x19MxGateway.Contracts.Protob\x06proto3"
|
"&WORKER_FAULT_CATEGORY_SHUTDOWN_TIMEOUT\x10\vB&\xaa\x02#ZB.MOM.WW.MxGateway.Contracts.Protob\x06proto3"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
file_mxaccess_worker_proto_rawDescOnce sync.Once
|
file_mxaccess_worker_proto_rawDescOnce sync.Once
|
||||||
|
|||||||
@@ -51,3 +51,26 @@ func (c *Client) QueryActiveAlarms(ctx context.Context, req *QueryActiveAlarmsRe
|
|||||||
|
|
||||||
return stream, nil
|
return stream, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StreamAlarms attaches to the gateway's central alarm feed. The stream opens
|
||||||
|
// with one AlarmFeedMessage per currently-active alarm (the ConditionRefresh
|
||||||
|
// snapshot), then a single snapshot-complete sentinel, then a transition for
|
||||||
|
// every subsequent raise / acknowledge / clear. It is served by the gateway's
|
||||||
|
// always-on alarm monitor — no worker session is opened — so any number of
|
||||||
|
// clients may attach.
|
||||||
|
//
|
||||||
|
// The returned stream is owned by the caller; cancel ctx to release it.
|
||||||
|
// Optional alarm-reference prefix scoping (req.AlarmFilterPrefix) limits the
|
||||||
|
// stream to a sub-tree.
|
||||||
|
func (c *Client) StreamAlarms(ctx context.Context, req *StreamAlarmsRequest) (StreamAlarmsClient, error) {
|
||||||
|
if req == nil {
|
||||||
|
return nil, errors.New("mxgateway: stream alarms request is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
stream, err := c.raw.StreamAlarms(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &GatewayError{Op: "stream alarms", Err: err}
|
||||||
|
}
|
||||||
|
|
||||||
|
return stream, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import (
|
|||||||
func TestAcknowledgeAlarmSendsRequestAndReturnsReply(t *testing.T) {
|
func TestAcknowledgeAlarmSendsRequestAndReturnsReply(t *testing.T) {
|
||||||
fake := &fakeGatewayWithAlarms{
|
fake := &fakeGatewayWithAlarms{
|
||||||
acknowledgeReply: &pb.AcknowledgeAlarmReply{
|
acknowledgeReply: &pb.AcknowledgeAlarmReply{
|
||||||
SessionId: "session-1",
|
|
||||||
CorrelationId: "corr-1",
|
CorrelationId: "corr-1",
|
||||||
ProtocolStatus: &pb.ProtocolStatus{
|
ProtocolStatus: &pb.ProtocolStatus{
|
||||||
Code: pb.ProtocolStatusCode_PROTOCOL_STATUS_CODE_OK,
|
Code: pb.ProtocolStatusCode_PROTOCOL_STATUS_CODE_OK,
|
||||||
@@ -35,7 +34,6 @@ func TestAcknowledgeAlarmSendsRequestAndReturnsReply(t *testing.T) {
|
|||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
reply, err := client.AcknowledgeAlarm(context.Background(), &pb.AcknowledgeAlarmRequest{
|
reply, err := client.AcknowledgeAlarm(context.Background(), &pb.AcknowledgeAlarmRequest{
|
||||||
SessionId: "session-1",
|
|
||||||
ClientCorrelationId: "corr-1",
|
ClientCorrelationId: "corr-1",
|
||||||
AlarmFullReference: "Tank01.Level.HiHi",
|
AlarmFullReference: "Tank01.Level.HiHi",
|
||||||
Comment: "investigating",
|
Comment: "investigating",
|
||||||
@@ -81,7 +79,6 @@ func TestAcknowledgeAlarmMapsUnauthenticated(t *testing.T) {
|
|||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
_, err := client.AcknowledgeAlarm(context.Background(), &pb.AcknowledgeAlarmRequest{
|
_, err := client.AcknowledgeAlarm(context.Background(), &pb.AcknowledgeAlarmRequest{
|
||||||
SessionId: "session-1",
|
|
||||||
AlarmFullReference: "Tank01.Level.HiHi",
|
AlarmFullReference: "Tank01.Level.HiHi",
|
||||||
OperatorUser: "alice",
|
OperatorUser: "alice",
|
||||||
})
|
})
|
||||||
@@ -150,8 +147,8 @@ func TestQueryActiveAlarmsPassesFilterPrefix(t *testing.T) {
|
|||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
stream, err := client.QueryActiveAlarms(context.Background(), &pb.QueryActiveAlarmsRequest{
|
stream, err := client.QueryActiveAlarms(context.Background(), &pb.QueryActiveAlarmsRequest{
|
||||||
SessionId: "session-1",
|
SessionId: "session-1",
|
||||||
AlarmFilterPrefix: "Tank01.",
|
AlarmFilterPrefix: "Tank01.",
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("QueryActiveAlarms() error = %v", err)
|
t.Fatalf("QueryActiveAlarms() error = %v", err)
|
||||||
@@ -193,7 +190,7 @@ func (s *fakeGatewayWithAlarms) AcknowledgeAlarm(ctx context.Context, req *pb.Ac
|
|||||||
return s.acknowledgeReply, nil
|
return s.acknowledgeReply, nil
|
||||||
}
|
}
|
||||||
return &pb.AcknowledgeAlarmReply{
|
return &pb.AcknowledgeAlarmReply{
|
||||||
SessionId: req.GetSessionId(),
|
CorrelationId: req.GetClientCorrelationId(),
|
||||||
ProtocolStatus: &pb.ProtocolStatus{
|
ProtocolStatus: &pb.ProtocolStatus{
|
||||||
Code: pb.ProtocolStatusCode_PROTOCOL_STATUS_CODE_OK,
|
Code: pb.ProtocolStatusCode_PROTOCOL_STATUS_CODE_OK,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -55,8 +55,8 @@ func TestGalaxyGetLastDeployTimeReturnsTimestampWhenPresent(t *testing.T) {
|
|||||||
want := time.Date(2026, 4, 28, 12, 34, 56, 0, time.UTC)
|
want := time.Date(2026, 4, 28, 12, 34, 56, 0, time.UTC)
|
||||||
fake := &fakeGalaxyServer{
|
fake := &fakeGalaxyServer{
|
||||||
deployReply: &pb.GetLastDeployTimeReply{
|
deployReply: &pb.GetLastDeployTimeReply{
|
||||||
Present: true,
|
Present: true,
|
||||||
TimeOfLastDeploy: timestamppb.New(want),
|
TimeOfLastDeploy: timestamppb.New(want),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
client, cleanup := newGalaxyBufconnClient(t, fake)
|
client, cleanup := newGalaxyBufconnClient(t, fake)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
pb "gitea.dohertylan.com/dohertj2/mxaccessgw/clients/go/internal/generated"
|
pb "gitea.dohertylan.com/dohertj2/mxaccessgw/clients/go/internal/generated"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
@@ -387,6 +388,142 @@ func (s *Session) UnsubscribeBulk(ctx context.Context, serverHandle int32, itemH
|
|||||||
return reply.GetUnsubscribeBulk().GetResults(), nil
|
return reply.GetUnsubscribeBulk().GetResults(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteBulk invokes MXAccess Write sequentially for each entry inside one gateway command.
|
||||||
|
// Per-entry failures appear as BulkWriteResult entries with WasSuccessful=false; the call
|
||||||
|
// never returns an error for per-entry MXAccess failures (it returns an error only for
|
||||||
|
// protocol-level failures or transport errors).
|
||||||
|
func (s *Session) WriteBulk(ctx context.Context, serverHandle int32, entries []*WriteBulkEntry) ([]*BulkWriteResult, error) {
|
||||||
|
if entries == nil {
|
||||||
|
return nil, errors.New("mxgateway: write bulk entries are required")
|
||||||
|
}
|
||||||
|
if err := ensureBulkSize("write bulk entries", len(entries)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
reply, err := s.invokeCommand(ctx, &pb.MxCommand{
|
||||||
|
Kind: pb.MxCommandKind_MX_COMMAND_KIND_WRITE_BULK,
|
||||||
|
Payload: &pb.MxCommand_WriteBulk{
|
||||||
|
WriteBulk: &pb.WriteBulkCommand{
|
||||||
|
ServerHandle: serverHandle,
|
||||||
|
Entries: entries,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return reply.GetWriteBulk().GetResults(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write2Bulk invokes MXAccess Write2 (timestamped) for each entry inside one gateway command.
|
||||||
|
func (s *Session) Write2Bulk(ctx context.Context, serverHandle int32, entries []*Write2BulkEntry) ([]*BulkWriteResult, error) {
|
||||||
|
if entries == nil {
|
||||||
|
return nil, errors.New("mxgateway: write2 bulk entries are required")
|
||||||
|
}
|
||||||
|
if err := ensureBulkSize("write2 bulk entries", len(entries)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
reply, err := s.invokeCommand(ctx, &pb.MxCommand{
|
||||||
|
Kind: pb.MxCommandKind_MX_COMMAND_KIND_WRITE2_BULK,
|
||||||
|
Payload: &pb.MxCommand_Write2Bulk{
|
||||||
|
Write2Bulk: &pb.Write2BulkCommand{
|
||||||
|
ServerHandle: serverHandle,
|
||||||
|
Entries: entries,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return reply.GetWrite2Bulk().GetResults(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteSecuredBulk invokes MXAccess WriteSecured for each entry. Credential-sensitive
|
||||||
|
// values must not be logged by callers; mirrors the single-item WriteSecured contract.
|
||||||
|
func (s *Session) WriteSecuredBulk(ctx context.Context, serverHandle int32, entries []*WriteSecuredBulkEntry) ([]*BulkWriteResult, error) {
|
||||||
|
if entries == nil {
|
||||||
|
return nil, errors.New("mxgateway: write-secured bulk entries are required")
|
||||||
|
}
|
||||||
|
if err := ensureBulkSize("write-secured bulk entries", len(entries)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
reply, err := s.invokeCommand(ctx, &pb.MxCommand{
|
||||||
|
Kind: pb.MxCommandKind_MX_COMMAND_KIND_WRITE_SECURED_BULK,
|
||||||
|
Payload: &pb.MxCommand_WriteSecuredBulk{
|
||||||
|
WriteSecuredBulk: &pb.WriteSecuredBulkCommand{
|
||||||
|
ServerHandle: serverHandle,
|
||||||
|
Entries: entries,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return reply.GetWriteSecuredBulk().GetResults(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteSecured2Bulk invokes MXAccess WriteSecured2 (timestamped) for each entry.
|
||||||
|
func (s *Session) WriteSecured2Bulk(ctx context.Context, serverHandle int32, entries []*WriteSecured2BulkEntry) ([]*BulkWriteResult, error) {
|
||||||
|
if entries == nil {
|
||||||
|
return nil, errors.New("mxgateway: write-secured2 bulk entries are required")
|
||||||
|
}
|
||||||
|
if err := ensureBulkSize("write-secured2 bulk entries", len(entries)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
reply, err := s.invokeCommand(ctx, &pb.MxCommand{
|
||||||
|
Kind: pb.MxCommandKind_MX_COMMAND_KIND_WRITE_SECURED2_BULK,
|
||||||
|
Payload: &pb.MxCommand_WriteSecured2Bulk{
|
||||||
|
WriteSecured2Bulk: &pb.WriteSecured2BulkCommand{
|
||||||
|
ServerHandle: serverHandle,
|
||||||
|
Entries: entries,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return reply.GetWriteSecured2Bulk().GetResults(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadBulk snapshots the current value of each requested tag.
|
||||||
|
//
|
||||||
|
// MXAccess COM has no synchronous Read; the worker satisfies this by returning the
|
||||||
|
// most recent cached OnDataChange value when the tag is already advised (WasCached=true),
|
||||||
|
// or by taking a full AddItem + Advise + wait + UnAdvise + RemoveItem snapshot lifecycle
|
||||||
|
// otherwise. timeout bounds the wait per tag in the snapshot case; pass zero to use the
|
||||||
|
// worker default. Per-tag failures (timeout, invalid tag) appear as BulkReadResult entries
|
||||||
|
// with WasSuccessful=false; the call never returns an error for per-tag MXAccess failures.
|
||||||
|
func (s *Session) ReadBulk(ctx context.Context, serverHandle int32, tagAddresses []string, timeout time.Duration) ([]*BulkReadResult, error) {
|
||||||
|
if tagAddresses == nil {
|
||||||
|
return nil, errors.New("mxgateway: tag addresses are required")
|
||||||
|
}
|
||||||
|
if err := ensureBulkSize("tag addresses", len(tagAddresses)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var timeoutMs uint32
|
||||||
|
if timeout > 0 {
|
||||||
|
ms := timeout.Milliseconds()
|
||||||
|
if ms > int64(^uint32(0)) {
|
||||||
|
timeoutMs = ^uint32(0)
|
||||||
|
} else {
|
||||||
|
timeoutMs = uint32(ms)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reply, err := s.invokeCommand(ctx, &pb.MxCommand{
|
||||||
|
Kind: pb.MxCommandKind_MX_COMMAND_KIND_READ_BULK,
|
||||||
|
Payload: &pb.MxCommand_ReadBulk{
|
||||||
|
ReadBulk: &pb.ReadBulkCommand{
|
||||||
|
ServerHandle: serverHandle,
|
||||||
|
TagAddresses: tagAddresses,
|
||||||
|
TimeoutMs: timeoutMs,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return reply.GetReadBulk().GetResults(), nil
|
||||||
|
}
|
||||||
|
|
||||||
// Write invokes MXAccess Write.
|
// Write invokes MXAccess Write.
|
||||||
func (s *Session) Write(ctx context.Context, serverHandle, itemHandle int32, value *MxValue, userID int32) error {
|
func (s *Session) Write(ctx context.Context, serverHandle, itemHandle int32, value *MxValue, userID int32) error {
|
||||||
_, err := s.WriteRaw(ctx, serverHandle, itemHandle, value, userID)
|
_, err := s.WriteRaw(ctx, serverHandle, itemHandle, value, userID)
|
||||||
|
|||||||
@@ -70,6 +70,32 @@ type (
|
|||||||
WriteCommand = pb.WriteCommand
|
WriteCommand = pb.WriteCommand
|
||||||
// Write2Command is the payload of an MXAccess Write2 command.
|
// Write2Command is the payload of an MXAccess Write2 command.
|
||||||
Write2Command = pb.Write2Command
|
Write2Command = pb.Write2Command
|
||||||
|
// WriteBulkCommand is the payload of a bulk Write command.
|
||||||
|
WriteBulkCommand = pb.WriteBulkCommand
|
||||||
|
// WriteBulkEntry is one entry inside a WriteBulkCommand.
|
||||||
|
WriteBulkEntry = pb.WriteBulkEntry
|
||||||
|
// Write2BulkCommand is the payload of a bulk Write2 (timestamped) command.
|
||||||
|
Write2BulkCommand = pb.Write2BulkCommand
|
||||||
|
// Write2BulkEntry is one entry inside a Write2BulkCommand.
|
||||||
|
Write2BulkEntry = pb.Write2BulkEntry
|
||||||
|
// WriteSecuredBulkCommand is the payload of a bulk WriteSecured command.
|
||||||
|
WriteSecuredBulkCommand = pb.WriteSecuredBulkCommand
|
||||||
|
// WriteSecuredBulkEntry is one entry inside a WriteSecuredBulkCommand.
|
||||||
|
WriteSecuredBulkEntry = pb.WriteSecuredBulkEntry
|
||||||
|
// WriteSecured2BulkCommand is the payload of a bulk WriteSecured2 (timestamped) command.
|
||||||
|
WriteSecured2BulkCommand = pb.WriteSecured2BulkCommand
|
||||||
|
// WriteSecured2BulkEntry is one entry inside a WriteSecured2BulkCommand.
|
||||||
|
WriteSecured2BulkEntry = pb.WriteSecured2BulkEntry
|
||||||
|
// ReadBulkCommand is the payload of a bulk Read snapshot command.
|
||||||
|
ReadBulkCommand = pb.ReadBulkCommand
|
||||||
|
// BulkWriteReply aggregates BulkWriteResult entries for a bulk write command.
|
||||||
|
BulkWriteReply = pb.BulkWriteReply
|
||||||
|
// BulkWriteResult is one entry in a bulk write reply list.
|
||||||
|
BulkWriteResult = pb.BulkWriteResult
|
||||||
|
// BulkReadReply aggregates BulkReadResult entries for a bulk read command.
|
||||||
|
BulkReadReply = pb.BulkReadReply
|
||||||
|
// BulkReadResult is one entry in a bulk read reply list.
|
||||||
|
BulkReadResult = pb.BulkReadResult
|
||||||
// RegisterReply carries the ServerHandle returned by Register.
|
// RegisterReply carries the ServerHandle returned by Register.
|
||||||
RegisterReply = pb.RegisterReply
|
RegisterReply = pb.RegisterReply
|
||||||
// AddItemReply carries the ItemHandle returned by AddItem.
|
// AddItemReply carries the ItemHandle returned by AddItem.
|
||||||
@@ -86,6 +112,11 @@ type (
|
|||||||
AcknowledgeAlarmReply = pb.AcknowledgeAlarmReply
|
AcknowledgeAlarmReply = pb.AcknowledgeAlarmReply
|
||||||
// QueryActiveAlarmsRequest is the gateway QueryActiveAlarms request message.
|
// QueryActiveAlarmsRequest is the gateway QueryActiveAlarms request message.
|
||||||
QueryActiveAlarmsRequest = pb.QueryActiveAlarmsRequest
|
QueryActiveAlarmsRequest = pb.QueryActiveAlarmsRequest
|
||||||
|
// StreamAlarmsRequest is the gateway StreamAlarms request message.
|
||||||
|
StreamAlarmsRequest = pb.StreamAlarmsRequest
|
||||||
|
// AlarmFeedMessage is one message on the StreamAlarms feed — an
|
||||||
|
// active-alarm snapshot row, a snapshot-complete sentinel, or a transition.
|
||||||
|
AlarmFeedMessage = pb.AlarmFeedMessage
|
||||||
// ActiveAlarmSnapshot is one row in a ConditionRefresh stream.
|
// ActiveAlarmSnapshot is one row in a ConditionRefresh stream.
|
||||||
ActiveAlarmSnapshot = pb.ActiveAlarmSnapshot
|
ActiveAlarmSnapshot = pb.ActiveAlarmSnapshot
|
||||||
// OnAlarmTransitionEvent is the body carried by alarm-transition MxEvents.
|
// OnAlarmTransitionEvent is the body carried by alarm-transition MxEvents.
|
||||||
@@ -104,6 +135,10 @@ type AlarmConditionState = pb.AlarmConditionState
|
|||||||
// QueryActiveAlarms RPC.
|
// QueryActiveAlarms RPC.
|
||||||
type QueryActiveAlarmsClient = pb.MxAccessGateway_QueryActiveAlarmsClient
|
type QueryActiveAlarmsClient = pb.MxAccessGateway_QueryActiveAlarmsClient
|
||||||
|
|
||||||
|
// StreamAlarmsClient is the generated server-streaming client for the
|
||||||
|
// StreamAlarms RPC.
|
||||||
|
type StreamAlarmsClient = pb.MxAccessGateway_StreamAlarmsClient
|
||||||
|
|
||||||
// Enumerations from the generated contract re-exported for client callers.
|
// Enumerations from the generated contract re-exported for client callers.
|
||||||
type (
|
type (
|
||||||
// MxCommandKind discriminates which MXAccess command an MxCommand carries.
|
// MxCommandKind discriminates which MXAccess command an MxCommand carries.
|
||||||
|
|||||||
@@ -18,13 +18,13 @@ clients/java/
|
|||||||
settings.gradle
|
settings.gradle
|
||||||
build.gradle
|
build.gradle
|
||||||
src/main/generated/
|
src/main/generated/
|
||||||
mxgateway-client/
|
zb-mom-ww-mxgateway-client/
|
||||||
build.gradle
|
build.gradle
|
||||||
src/main/java/com/dohertylan/mxgateway/client/
|
src/main/java/com/zb/mom/ww/mxgateway/client/
|
||||||
src/test/java/com/dohertylan/mxgateway/client/
|
src/test/java/com/zb/mom/ww/mxgateway/client/
|
||||||
mxgateway-cli/
|
zb-mom-ww-mxgateway-cli/
|
||||||
build.gradle
|
build.gradle
|
||||||
src/main/java/com/dohertylan/mxgateway/cli/
|
src/main/java/com/zb/mom/ww/mxgateway/cli/
|
||||||
```
|
```
|
||||||
|
|
||||||
Alternative Maven layout is acceptable if the repo standardizes on Maven.
|
Alternative Maven layout is acceptable if the repo standardizes on Maven.
|
||||||
@@ -192,8 +192,8 @@ stream for bounded time, and close.
|
|||||||
|
|
||||||
Publish library and CLI separately:
|
Publish library and CLI separately:
|
||||||
|
|
||||||
- `mxgateway-client` jar,
|
- `zb-mom-ww-mxgateway-client` jar,
|
||||||
- `mxgateway-cli` runnable distribution.
|
- `zb-mom-ww-mxgateway-cli` runnable distribution.
|
||||||
|
|
||||||
Generated protobuf code should be produced during the build from shared proto
|
Generated protobuf code should be produced during the build from shared proto
|
||||||
files and should not be hand-edited.
|
files and should not be hand-edited.
|
||||||
@@ -206,10 +206,10 @@ Run the Java scaffold checks from `clients/java`:
|
|||||||
gradle test
|
gradle test
|
||||||
```
|
```
|
||||||
|
|
||||||
The `mxgateway-client` project generates the gateway and worker protobuf/gRPC
|
The `zb-mom-ww-mxgateway-client` project generates the gateway and worker
|
||||||
bindings into `src/main/generated`, compiles the generated contracts, and runs
|
protobuf/gRPC bindings into `src/main/generated`, compiles the generated
|
||||||
JUnit 5 tests. The `mxgateway-cli` project builds a Picocli-based `mxgw-java`
|
contracts, and runs JUnit 5 tests. The `zb-mom-ww-mxgateway-cli` project
|
||||||
entry point for later command implementation.
|
builds a Picocli-based `mxgw-java` entry point for later command implementation.
|
||||||
|
|
||||||
## Related Documentation
|
## Related Documentation
|
||||||
|
|
||||||
|
|||||||
+36
-27
@@ -10,22 +10,23 @@ clients/java/
|
|||||||
settings.gradle
|
settings.gradle
|
||||||
build.gradle
|
build.gradle
|
||||||
src/main/generated/
|
src/main/generated/
|
||||||
mxgateway-client/
|
zb-mom-ww-mxgateway-client/
|
||||||
mxgateway-cli/
|
zb-mom-ww-mxgateway-cli/
|
||||||
```
|
```
|
||||||
|
|
||||||
`mxgateway-client` generates Java protobuf and gRPC sources from
|
`zb-mom-ww-mxgateway-client` generates Java protobuf and gRPC sources from
|
||||||
`../../src/MxGateway.Contracts/Protos`. The Gradle protobuf plugin writes those
|
`../../src/ZB.MOM.WW.MxGateway.Contracts/Protos`. The Gradle protobuf plugin writes those
|
||||||
generated sources under `src/main/generated`, which matches the client proto
|
generated sources under `src/main/generated`, which matches the client proto
|
||||||
manifest in `../proto/proto-inputs.json`. Do not edit generated files by hand.
|
manifest in `../proto/proto-inputs.json`. Do not edit generated files by hand.
|
||||||
|
|
||||||
`mxgateway-client` exposes `MxGatewayClientOptions`, `MxGatewayClient`,
|
`zb-mom-ww-mxgateway-client` exposes `MxGatewayClientOptions`, `MxGatewayClient`,
|
||||||
`MxGatewaySession`, value/status helpers, typed gateway exceptions, raw
|
`MxGatewaySession`, value/status helpers, typed gateway exceptions, raw
|
||||||
generated stubs, and generated protobuf messages for parity tests.
|
generated stubs, and generated protobuf messages for parity tests.
|
||||||
|
|
||||||
`mxgateway-cli` depends on `mxgateway-client` and provides the `mxgw-java`
|
`zb-mom-ww-mxgateway-cli` depends on `zb-mom-ww-mxgateway-client` and provides
|
||||||
application entry point. The CLI supports version, session, command, event
|
the `mxgw-java` application entry point. The CLI supports version, session,
|
||||||
streaming, write, and smoke-test commands with deterministic JSON output.
|
command, event streaming, write, and smoke-test commands with deterministic
|
||||||
|
JSON output.
|
||||||
|
|
||||||
## Regenerating Protobuf Bindings
|
## Regenerating Protobuf Bindings
|
||||||
|
|
||||||
@@ -33,7 +34,7 @@ Run generation from `clients/java` after the shared `.proto` files or Java
|
|||||||
output path changes:
|
output path changes:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
gradle :mxgateway-client:generateProto
|
gradle :zb-mom-ww-mxgateway-client:generateProto
|
||||||
```
|
```
|
||||||
|
|
||||||
## Client Usage
|
## Client Usage
|
||||||
@@ -67,6 +68,12 @@ cancels the underlying gRPC stream. Canceling or timing out a Java client call
|
|||||||
only stops the client from waiting; it does not abort an in-flight MXAccess COM
|
only stops the client from waiting; it does not abort an in-flight MXAccess COM
|
||||||
call on the worker STA.
|
call on the worker STA.
|
||||||
|
|
||||||
|
For alarms, `MxGatewayClient` exposes `queryActiveAlarms` (one-shot snapshot),
|
||||||
|
`streamAlarms` (returns an `MxGatewayAlarmFeedSubscription` whose iterator
|
||||||
|
yields alarm-feed messages from the gateway's central monitor), and
|
||||||
|
`acknowledgeAlarm` (ack by full alarm reference with an optional comment and
|
||||||
|
ack target). Close the subscription to cancel the underlying gRPC stream.
|
||||||
|
|
||||||
## Galaxy Repository Browse
|
## Galaxy Repository Browse
|
||||||
|
|
||||||
The Galaxy Repository service is a separate metadata-only gRPC service exposed
|
The Galaxy Repository service is a separate metadata-only gRPC service exposed
|
||||||
@@ -104,9 +111,9 @@ The CLI exposes matching subcommands: `galaxy-test`, `galaxy-deploy-time`,
|
|||||||
`--timeout`, and `--json` options as the gateway commands.
|
`--timeout`, and `--json` options as the gateway commands.
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
gradle :mxgateway-cli:run --args="galaxy-test --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --json"
|
gradle :zb-mom-ww-mxgateway-cli:run --args="galaxy-test --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --json"
|
||||||
gradle :mxgateway-cli:run --args="galaxy-deploy-time --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --json"
|
gradle :zb-mom-ww-mxgateway-cli:run --args="galaxy-deploy-time --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --json"
|
||||||
gradle :mxgateway-cli:run --args="galaxy-discover --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --json"
|
gradle :zb-mom-ww-mxgateway-cli:run --args="galaxy-discover --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --json"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Watching deploy events
|
### Watching deploy events
|
||||||
@@ -156,8 +163,8 @@ The matching CLI subcommand streams events until cancelled (Ctrl+C) and prints
|
|||||||
one line per event in text mode or one JSON object per event with `--json`:
|
one line per event in text mode or one JSON object per event with `--json`:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
gradle :mxgateway-cli:run --args="galaxy-watch --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --json"
|
gradle :zb-mom-ww-mxgateway-cli:run --args="galaxy-watch --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --json"
|
||||||
gradle :mxgateway-cli:run --args="galaxy-watch --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --last-seen-deploy-time 2026-04-28T18:30:00Z --limit 5"
|
gradle :zb-mom-ww-mxgateway-cli:run --args="galaxy-watch --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --last-seen-deploy-time 2026-04-28T18:30:00Z --limit 5"
|
||||||
```
|
```
|
||||||
|
|
||||||
## CLI Usage
|
## CLI Usage
|
||||||
@@ -165,14 +172,16 @@ gradle :mxgateway-cli:run --args="galaxy-watch --endpoint localhost:5000 --api-k
|
|||||||
Run the CLI through Gradle:
|
Run the CLI through Gradle:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
gradle :mxgateway-cli:run --args="version --json"
|
gradle :zb-mom-ww-mxgateway-cli:run --args="version --json"
|
||||||
gradle :mxgateway-cli:run --args="open-session --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --client-session-name java-cli --json"
|
gradle :zb-mom-ww-mxgateway-cli:run --args="open-session --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --client-session-name java-cli --json"
|
||||||
gradle :mxgateway-cli:run --args="register --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --session-id <id> --client-name java-cli --json"
|
gradle :zb-mom-ww-mxgateway-cli:run --args="register --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --session-id <id> --client-name java-cli --json"
|
||||||
gradle :mxgateway-cli:run --args="add-item --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --session-id <id> --server-handle 1 --item TestObject.TestInt --json"
|
gradle :zb-mom-ww-mxgateway-cli:run --args="add-item --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --session-id <id> --server-handle 1 --item TestObject.TestInt --json"
|
||||||
gradle :mxgateway-cli:run --args="advise --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --session-id <id> --server-handle 1 --item-handle 1 --json"
|
gradle :zb-mom-ww-mxgateway-cli:run --args="advise --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --session-id <id> --server-handle 1 --item-handle 1 --json"
|
||||||
gradle :mxgateway-cli:run --args="write --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --session-id <id> --server-handle 1 --item-handle 1 --type int32 --value 123 --json"
|
gradle :zb-mom-ww-mxgateway-cli:run --args="write --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --session-id <id> --server-handle 1 --item-handle 1 --type int32 --value 123 --json"
|
||||||
gradle :mxgateway-cli:run --args="stream-events --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --session-id <id> --limit 1 --json"
|
gradle :zb-mom-ww-mxgateway-cli:run --args="stream-events --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --session-id <id> --limit 1 --json"
|
||||||
gradle :mxgateway-cli:run --args="smoke --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --item TestObject.TestInt --json"
|
gradle :zb-mom-ww-mxgateway-cli:run --args="stream-alarms --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --session-id <id> --limit 1 --json"
|
||||||
|
gradle :zb-mom-ww-mxgateway-cli:run --args="acknowledge-alarm --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --session-id <id> --alarm-reference \"\\Galaxy\Area001.Pump001.PumpFault\" --json"
|
||||||
|
gradle :zb-mom-ww-mxgateway-cli:run --args="smoke --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --item TestObject.TestInt --json"
|
||||||
```
|
```
|
||||||
|
|
||||||
The CLI accepts `--api-key`, `--api-key-env`, `--plaintext`, `--ca-file`,
|
The CLI accepts `--api-key`, `--api-key-env`, `--plaintext`, `--ca-file`,
|
||||||
@@ -182,7 +191,7 @@ output redacts API keys.
|
|||||||
Use TLS options for a secured gateway:
|
Use TLS options for a secured gateway:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
gradle :mxgateway-cli:run --args="smoke --endpoint mxgateway.example.local:5001 --ca-file C:\certs\mxgateway-ca.pem --server-name-override mxgateway.example.local --api-key-env MXGATEWAY_API_KEY --item TestObject.TestInt --json"
|
gradle :zb-mom-ww-mxgateway-cli:run --args="smoke --endpoint mxgateway.example.local:5001 --ca-file C:\certs\mxgateway-ca.pem --server-name-override mxgateway.example.local --api-key-env MXGATEWAY_API_KEY --item TestObject.TestInt --json"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Build And Test
|
## Build And Test
|
||||||
@@ -202,11 +211,11 @@ in-process gRPC behavior, stream cancellation, and CLI parser/output behavior.
|
|||||||
Create local library and CLI artifacts from `clients/java`:
|
Create local library and CLI artifacts from `clients/java`:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
gradle :mxgateway-client:jar :mxgateway-cli:installDist
|
gradle :zb-mom-ww-mxgateway-client:jar :zb-mom-ww-mxgateway-cli:installDist
|
||||||
```
|
```
|
||||||
|
|
||||||
The library jar is under `mxgateway-client/build/libs`. The installed CLI
|
The library jar is under `zb-mom-ww-mxgateway-client/build/libs`. The installed CLI
|
||||||
distribution is under `mxgateway-cli/build/install/mxgateway-cli`.
|
distribution is under `zb-mom-ww-mxgateway-cli/build/install/zb-mom-ww-mxgateway-cli`.
|
||||||
|
|
||||||
## Integration Checks
|
## Integration Checks
|
||||||
|
|
||||||
@@ -217,7 +226,7 @@ $env:MXGATEWAY_INTEGRATION = '1'
|
|||||||
$env:MXGATEWAY_ENDPOINT = 'localhost:5000'
|
$env:MXGATEWAY_ENDPOINT = 'localhost:5000'
|
||||||
$env:MXGATEWAY_API_KEY = '<gateway-api-key>'
|
$env:MXGATEWAY_API_KEY = '<gateway-api-key>'
|
||||||
$env:MXGATEWAY_TEST_ITEM = 'TestObject.TestInt'
|
$env:MXGATEWAY_TEST_ITEM = 'TestObject.TestInt'
|
||||||
gradle :mxgateway-cli:run --args="smoke --endpoint $env:MXGATEWAY_ENDPOINT --plaintext --api-key-env MXGATEWAY_API_KEY --item $env:MXGATEWAY_TEST_ITEM --json"
|
gradle :zb-mom-ww-mxgateway-cli:run --args="smoke --endpoint $env:MXGATEWAY_ENDPOINT --plaintext --api-key-env MXGATEWAY_API_KEY --item $env:MXGATEWAY_TEST_ITEM --json"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Related Documentation
|
## Related Documentation
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ ext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
subprojects {
|
subprojects {
|
||||||
group = 'com.dohertylan.mxgateway'
|
group = 'com.zb.mom.ww.mxgateway'
|
||||||
version = '0.1.0'
|
version = '0.1.0'
|
||||||
|
|
||||||
pluginManager.withPlugin('java') {
|
pluginManager.withPlugin('java') {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ dependencyResolutionManagement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rootProject.name = 'mxaccessgw-java'
|
rootProject.name = 'zb-mom-ww-mxaccessgw-java'
|
||||||
|
|
||||||
include 'mxgateway-client'
|
include 'zb-mom-ww-mxgateway-client'
|
||||||
include 'mxgateway-cli'
|
include 'zb-mom-ww-mxgateway-cli'
|
||||||
|
|||||||
+301
@@ -139,6 +139,99 @@ public final class MxAccessGatewayGrpc {
|
|||||||
return getStreamEventsMethod;
|
return getStreamEventsMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static volatile io.grpc.MethodDescriptor<mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmRequest,
|
||||||
|
mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmReply> getAcknowledgeAlarmMethod;
|
||||||
|
|
||||||
|
@io.grpc.stub.annotations.RpcMethod(
|
||||||
|
fullMethodName = SERVICE_NAME + '/' + "AcknowledgeAlarm",
|
||||||
|
requestType = mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmRequest.class,
|
||||||
|
responseType = mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmReply.class,
|
||||||
|
methodType = io.grpc.MethodDescriptor.MethodType.UNARY)
|
||||||
|
public static io.grpc.MethodDescriptor<mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmRequest,
|
||||||
|
mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmReply> getAcknowledgeAlarmMethod() {
|
||||||
|
io.grpc.MethodDescriptor<mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmRequest, mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmReply> getAcknowledgeAlarmMethod;
|
||||||
|
if ((getAcknowledgeAlarmMethod = MxAccessGatewayGrpc.getAcknowledgeAlarmMethod) == null) {
|
||||||
|
synchronized (MxAccessGatewayGrpc.class) {
|
||||||
|
if ((getAcknowledgeAlarmMethod = MxAccessGatewayGrpc.getAcknowledgeAlarmMethod) == null) {
|
||||||
|
MxAccessGatewayGrpc.getAcknowledgeAlarmMethod = getAcknowledgeAlarmMethod =
|
||||||
|
io.grpc.MethodDescriptor.<mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmRequest, mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmReply>newBuilder()
|
||||||
|
.setType(io.grpc.MethodDescriptor.MethodType.UNARY)
|
||||||
|
.setFullMethodName(generateFullMethodName(SERVICE_NAME, "AcknowledgeAlarm"))
|
||||||
|
.setSampledToLocalTracing(true)
|
||||||
|
.setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
|
||||||
|
mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmRequest.getDefaultInstance()))
|
||||||
|
.setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
|
||||||
|
mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmReply.getDefaultInstance()))
|
||||||
|
.setSchemaDescriptor(new MxAccessGatewayMethodDescriptorSupplier("AcknowledgeAlarm"))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getAcknowledgeAlarmMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static volatile io.grpc.MethodDescriptor<mxaccess_gateway.v1.MxaccessGateway.StreamAlarmsRequest,
|
||||||
|
mxaccess_gateway.v1.MxaccessGateway.AlarmFeedMessage> getStreamAlarmsMethod;
|
||||||
|
|
||||||
|
@io.grpc.stub.annotations.RpcMethod(
|
||||||
|
fullMethodName = SERVICE_NAME + '/' + "StreamAlarms",
|
||||||
|
requestType = mxaccess_gateway.v1.MxaccessGateway.StreamAlarmsRequest.class,
|
||||||
|
responseType = mxaccess_gateway.v1.MxaccessGateway.AlarmFeedMessage.class,
|
||||||
|
methodType = io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING)
|
||||||
|
public static io.grpc.MethodDescriptor<mxaccess_gateway.v1.MxaccessGateway.StreamAlarmsRequest,
|
||||||
|
mxaccess_gateway.v1.MxaccessGateway.AlarmFeedMessage> getStreamAlarmsMethod() {
|
||||||
|
io.grpc.MethodDescriptor<mxaccess_gateway.v1.MxaccessGateway.StreamAlarmsRequest, mxaccess_gateway.v1.MxaccessGateway.AlarmFeedMessage> getStreamAlarmsMethod;
|
||||||
|
if ((getStreamAlarmsMethod = MxAccessGatewayGrpc.getStreamAlarmsMethod) == null) {
|
||||||
|
synchronized (MxAccessGatewayGrpc.class) {
|
||||||
|
if ((getStreamAlarmsMethod = MxAccessGatewayGrpc.getStreamAlarmsMethod) == null) {
|
||||||
|
MxAccessGatewayGrpc.getStreamAlarmsMethod = getStreamAlarmsMethod =
|
||||||
|
io.grpc.MethodDescriptor.<mxaccess_gateway.v1.MxaccessGateway.StreamAlarmsRequest, mxaccess_gateway.v1.MxaccessGateway.AlarmFeedMessage>newBuilder()
|
||||||
|
.setType(io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING)
|
||||||
|
.setFullMethodName(generateFullMethodName(SERVICE_NAME, "StreamAlarms"))
|
||||||
|
.setSampledToLocalTracing(true)
|
||||||
|
.setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
|
||||||
|
mxaccess_gateway.v1.MxaccessGateway.StreamAlarmsRequest.getDefaultInstance()))
|
||||||
|
.setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
|
||||||
|
mxaccess_gateway.v1.MxaccessGateway.AlarmFeedMessage.getDefaultInstance()))
|
||||||
|
.setSchemaDescriptor(new MxAccessGatewayMethodDescriptorSupplier("StreamAlarms"))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getStreamAlarmsMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static volatile io.grpc.MethodDescriptor<mxaccess_gateway.v1.MxaccessGateway.QueryActiveAlarmsRequest,
|
||||||
|
mxaccess_gateway.v1.MxaccessGateway.ActiveAlarmSnapshot> getQueryActiveAlarmsMethod;
|
||||||
|
|
||||||
|
@io.grpc.stub.annotations.RpcMethod(
|
||||||
|
fullMethodName = SERVICE_NAME + '/' + "QueryActiveAlarms",
|
||||||
|
requestType = mxaccess_gateway.v1.MxaccessGateway.QueryActiveAlarmsRequest.class,
|
||||||
|
responseType = mxaccess_gateway.v1.MxaccessGateway.ActiveAlarmSnapshot.class,
|
||||||
|
methodType = io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING)
|
||||||
|
public static io.grpc.MethodDescriptor<mxaccess_gateway.v1.MxaccessGateway.QueryActiveAlarmsRequest,
|
||||||
|
mxaccess_gateway.v1.MxaccessGateway.ActiveAlarmSnapshot> getQueryActiveAlarmsMethod() {
|
||||||
|
io.grpc.MethodDescriptor<mxaccess_gateway.v1.MxaccessGateway.QueryActiveAlarmsRequest, mxaccess_gateway.v1.MxaccessGateway.ActiveAlarmSnapshot> getQueryActiveAlarmsMethod;
|
||||||
|
if ((getQueryActiveAlarmsMethod = MxAccessGatewayGrpc.getQueryActiveAlarmsMethod) == null) {
|
||||||
|
synchronized (MxAccessGatewayGrpc.class) {
|
||||||
|
if ((getQueryActiveAlarmsMethod = MxAccessGatewayGrpc.getQueryActiveAlarmsMethod) == null) {
|
||||||
|
MxAccessGatewayGrpc.getQueryActiveAlarmsMethod = getQueryActiveAlarmsMethod =
|
||||||
|
io.grpc.MethodDescriptor.<mxaccess_gateway.v1.MxaccessGateway.QueryActiveAlarmsRequest, mxaccess_gateway.v1.MxaccessGateway.ActiveAlarmSnapshot>newBuilder()
|
||||||
|
.setType(io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING)
|
||||||
|
.setFullMethodName(generateFullMethodName(SERVICE_NAME, "QueryActiveAlarms"))
|
||||||
|
.setSampledToLocalTracing(true)
|
||||||
|
.setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
|
||||||
|
mxaccess_gateway.v1.MxaccessGateway.QueryActiveAlarmsRequest.getDefaultInstance()))
|
||||||
|
.setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
|
||||||
|
mxaccess_gateway.v1.MxaccessGateway.ActiveAlarmSnapshot.getDefaultInstance()))
|
||||||
|
.setSchemaDescriptor(new MxAccessGatewayMethodDescriptorSupplier("QueryActiveAlarms"))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getQueryActiveAlarmsMethod;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new async stub that supports all call types for the service
|
* Creates a new async stub that supports all call types for the service
|
||||||
*/
|
*/
|
||||||
@@ -232,6 +325,44 @@ public final class MxAccessGatewayGrpc {
|
|||||||
io.grpc.stub.StreamObserver<mxaccess_gateway.v1.MxaccessGateway.MxEvent> responseObserver) {
|
io.grpc.stub.StreamObserver<mxaccess_gateway.v1.MxaccessGateway.MxEvent> responseObserver) {
|
||||||
io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getStreamEventsMethod(), responseObserver);
|
io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getStreamEventsMethod(), responseObserver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
default void acknowledgeAlarm(mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmRequest request,
|
||||||
|
io.grpc.stub.StreamObserver<mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmReply> responseObserver) {
|
||||||
|
io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getAcknowledgeAlarmMethod(), responseObserver);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Session-less central alarm feed. The stream opens with the current
|
||||||
|
* active-alarm snapshot (one `active_alarm` per alarm), then a single
|
||||||
|
* `snapshot_complete`, then a `transition` for every subsequent change.
|
||||||
|
* Served by the gateway's always-on alarm monitor; any number of clients
|
||||||
|
* fan out from the single monitor without opening a worker session.
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
default void streamAlarms(mxaccess_gateway.v1.MxaccessGateway.StreamAlarmsRequest request,
|
||||||
|
io.grpc.stub.StreamObserver<mxaccess_gateway.v1.MxaccessGateway.AlarmFeedMessage> responseObserver) {
|
||||||
|
io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getStreamAlarmsMethod(), responseObserver);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Point-in-time snapshot of the currently-active alarm set served from the
|
||||||
|
* gateway's always-on alarm monitor cache (session-less). Used after a
|
||||||
|
* reconnect to seed Part 9 client state, or to reconcile alarms that may
|
||||||
|
* have been missed during a transport blip. Streamed so callers can
|
||||||
|
* begin processing without buffering the full set.
|
||||||
|
* `QueryActiveAlarmsRequest.alarm_filter_prefix` optionally narrows the
|
||||||
|
* snapshot to alarms whose `alarm_full_reference` starts with the given
|
||||||
|
* prefix; an empty prefix returns the full set.
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
default void queryActiveAlarms(mxaccess_gateway.v1.MxaccessGateway.QueryActiveAlarmsRequest request,
|
||||||
|
io.grpc.stub.StreamObserver<mxaccess_gateway.v1.MxaccessGateway.ActiveAlarmSnapshot> responseObserver) {
|
||||||
|
io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getQueryActiveAlarmsMethod(), responseObserver);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -298,6 +429,47 @@ public final class MxAccessGatewayGrpc {
|
|||||||
io.grpc.stub.ClientCalls.asyncServerStreamingCall(
|
io.grpc.stub.ClientCalls.asyncServerStreamingCall(
|
||||||
getChannel().newCall(getStreamEventsMethod(), getCallOptions()), request, responseObserver);
|
getChannel().newCall(getStreamEventsMethod(), getCallOptions()), request, responseObserver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public void acknowledgeAlarm(mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmRequest request,
|
||||||
|
io.grpc.stub.StreamObserver<mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmReply> responseObserver) {
|
||||||
|
io.grpc.stub.ClientCalls.asyncUnaryCall(
|
||||||
|
getChannel().newCall(getAcknowledgeAlarmMethod(), getCallOptions()), request, responseObserver);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Session-less central alarm feed. The stream opens with the current
|
||||||
|
* active-alarm snapshot (one `active_alarm` per alarm), then a single
|
||||||
|
* `snapshot_complete`, then a `transition` for every subsequent change.
|
||||||
|
* Served by the gateway's always-on alarm monitor; any number of clients
|
||||||
|
* fan out from the single monitor without opening a worker session.
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
public void streamAlarms(mxaccess_gateway.v1.MxaccessGateway.StreamAlarmsRequest request,
|
||||||
|
io.grpc.stub.StreamObserver<mxaccess_gateway.v1.MxaccessGateway.AlarmFeedMessage> responseObserver) {
|
||||||
|
io.grpc.stub.ClientCalls.asyncServerStreamingCall(
|
||||||
|
getChannel().newCall(getStreamAlarmsMethod(), getCallOptions()), request, responseObserver);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Point-in-time snapshot of the currently-active alarm set served from the
|
||||||
|
* gateway's always-on alarm monitor cache (session-less). Used after a
|
||||||
|
* reconnect to seed Part 9 client state, or to reconcile alarms that may
|
||||||
|
* have been missed during a transport blip. Streamed so callers can
|
||||||
|
* begin processing without buffering the full set.
|
||||||
|
* `QueryActiveAlarmsRequest.alarm_filter_prefix` optionally narrows the
|
||||||
|
* snapshot to alarms whose `alarm_full_reference` starts with the given
|
||||||
|
* prefix; an empty prefix returns the full set.
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
public void queryActiveAlarms(mxaccess_gateway.v1.MxaccessGateway.QueryActiveAlarmsRequest request,
|
||||||
|
io.grpc.stub.StreamObserver<mxaccess_gateway.v1.MxaccessGateway.ActiveAlarmSnapshot> responseObserver) {
|
||||||
|
io.grpc.stub.ClientCalls.asyncServerStreamingCall(
|
||||||
|
getChannel().newCall(getQueryActiveAlarmsMethod(), getCallOptions()), request, responseObserver);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -348,6 +520,48 @@ public final class MxAccessGatewayGrpc {
|
|||||||
return io.grpc.stub.ClientCalls.blockingV2ServerStreamingCall(
|
return io.grpc.stub.ClientCalls.blockingV2ServerStreamingCall(
|
||||||
getChannel(), getStreamEventsMethod(), getCallOptions(), request);
|
getChannel(), getStreamEventsMethod(), getCallOptions(), request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmReply acknowledgeAlarm(mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmRequest request) throws io.grpc.StatusException {
|
||||||
|
return io.grpc.stub.ClientCalls.blockingV2UnaryCall(
|
||||||
|
getChannel(), getAcknowledgeAlarmMethod(), getCallOptions(), request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Session-less central alarm feed. The stream opens with the current
|
||||||
|
* active-alarm snapshot (one `active_alarm` per alarm), then a single
|
||||||
|
* `snapshot_complete`, then a `transition` for every subsequent change.
|
||||||
|
* Served by the gateway's always-on alarm monitor; any number of clients
|
||||||
|
* fan out from the single monitor without opening a worker session.
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
@io.grpc.ExperimentalApi("https://github.com/grpc/grpc-java/issues/10918")
|
||||||
|
public io.grpc.stub.BlockingClientCall<?, mxaccess_gateway.v1.MxaccessGateway.AlarmFeedMessage>
|
||||||
|
streamAlarms(mxaccess_gateway.v1.MxaccessGateway.StreamAlarmsRequest request) {
|
||||||
|
return io.grpc.stub.ClientCalls.blockingV2ServerStreamingCall(
|
||||||
|
getChannel(), getStreamAlarmsMethod(), getCallOptions(), request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Point-in-time snapshot of the currently-active alarm set served from the
|
||||||
|
* gateway's always-on alarm monitor cache (session-less). Used after a
|
||||||
|
* reconnect to seed Part 9 client state, or to reconcile alarms that may
|
||||||
|
* have been missed during a transport blip. Streamed so callers can
|
||||||
|
* begin processing without buffering the full set.
|
||||||
|
* `QueryActiveAlarmsRequest.alarm_filter_prefix` optionally narrows the
|
||||||
|
* snapshot to alarms whose `alarm_full_reference` starts with the given
|
||||||
|
* prefix; an empty prefix returns the full set.
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
@io.grpc.ExperimentalApi("https://github.com/grpc/grpc-java/issues/10918")
|
||||||
|
public io.grpc.stub.BlockingClientCall<?, mxaccess_gateway.v1.MxaccessGateway.ActiveAlarmSnapshot>
|
||||||
|
queryActiveAlarms(mxaccess_gateway.v1.MxaccessGateway.QueryActiveAlarmsRequest request) {
|
||||||
|
return io.grpc.stub.ClientCalls.blockingV2ServerStreamingCall(
|
||||||
|
getChannel(), getQueryActiveAlarmsMethod(), getCallOptions(), request);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -397,6 +611,46 @@ public final class MxAccessGatewayGrpc {
|
|||||||
return io.grpc.stub.ClientCalls.blockingServerStreamingCall(
|
return io.grpc.stub.ClientCalls.blockingServerStreamingCall(
|
||||||
getChannel(), getStreamEventsMethod(), getCallOptions(), request);
|
getChannel(), getStreamEventsMethod(), getCallOptions(), request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmReply acknowledgeAlarm(mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmRequest request) {
|
||||||
|
return io.grpc.stub.ClientCalls.blockingUnaryCall(
|
||||||
|
getChannel(), getAcknowledgeAlarmMethod(), getCallOptions(), request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Session-less central alarm feed. The stream opens with the current
|
||||||
|
* active-alarm snapshot (one `active_alarm` per alarm), then a single
|
||||||
|
* `snapshot_complete`, then a `transition` for every subsequent change.
|
||||||
|
* Served by the gateway's always-on alarm monitor; any number of clients
|
||||||
|
* fan out from the single monitor without opening a worker session.
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
public java.util.Iterator<mxaccess_gateway.v1.MxaccessGateway.AlarmFeedMessage> streamAlarms(
|
||||||
|
mxaccess_gateway.v1.MxaccessGateway.StreamAlarmsRequest request) {
|
||||||
|
return io.grpc.stub.ClientCalls.blockingServerStreamingCall(
|
||||||
|
getChannel(), getStreamAlarmsMethod(), getCallOptions(), request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Point-in-time snapshot of the currently-active alarm set served from the
|
||||||
|
* gateway's always-on alarm monitor cache (session-less). Used after a
|
||||||
|
* reconnect to seed Part 9 client state, or to reconcile alarms that may
|
||||||
|
* have been missed during a transport blip. Streamed so callers can
|
||||||
|
* begin processing without buffering the full set.
|
||||||
|
* `QueryActiveAlarmsRequest.alarm_filter_prefix` optionally narrows the
|
||||||
|
* snapshot to alarms whose `alarm_full_reference` starts with the given
|
||||||
|
* prefix; an empty prefix returns the full set.
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
public java.util.Iterator<mxaccess_gateway.v1.MxaccessGateway.ActiveAlarmSnapshot> queryActiveAlarms(
|
||||||
|
mxaccess_gateway.v1.MxaccessGateway.QueryActiveAlarmsRequest request) {
|
||||||
|
return io.grpc.stub.ClientCalls.blockingServerStreamingCall(
|
||||||
|
getChannel(), getQueryActiveAlarmsMethod(), getCallOptions(), request);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -441,12 +695,23 @@ public final class MxAccessGatewayGrpc {
|
|||||||
return io.grpc.stub.ClientCalls.futureUnaryCall(
|
return io.grpc.stub.ClientCalls.futureUnaryCall(
|
||||||
getChannel().newCall(getInvokeMethod(), getCallOptions()), request);
|
getChannel().newCall(getInvokeMethod(), getCallOptions()), request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public com.google.common.util.concurrent.ListenableFuture<mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmReply> acknowledgeAlarm(
|
||||||
|
mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmRequest request) {
|
||||||
|
return io.grpc.stub.ClientCalls.futureUnaryCall(
|
||||||
|
getChannel().newCall(getAcknowledgeAlarmMethod(), getCallOptions()), request);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final int METHODID_OPEN_SESSION = 0;
|
private static final int METHODID_OPEN_SESSION = 0;
|
||||||
private static final int METHODID_CLOSE_SESSION = 1;
|
private static final int METHODID_CLOSE_SESSION = 1;
|
||||||
private static final int METHODID_INVOKE = 2;
|
private static final int METHODID_INVOKE = 2;
|
||||||
private static final int METHODID_STREAM_EVENTS = 3;
|
private static final int METHODID_STREAM_EVENTS = 3;
|
||||||
|
private static final int METHODID_ACKNOWLEDGE_ALARM = 4;
|
||||||
|
private static final int METHODID_STREAM_ALARMS = 5;
|
||||||
|
private static final int METHODID_QUERY_ACTIVE_ALARMS = 6;
|
||||||
|
|
||||||
private static final class MethodHandlers<Req, Resp> implements
|
private static final class MethodHandlers<Req, Resp> implements
|
||||||
io.grpc.stub.ServerCalls.UnaryMethod<Req, Resp>,
|
io.grpc.stub.ServerCalls.UnaryMethod<Req, Resp>,
|
||||||
@@ -481,6 +746,18 @@ public final class MxAccessGatewayGrpc {
|
|||||||
serviceImpl.streamEvents((mxaccess_gateway.v1.MxaccessGateway.StreamEventsRequest) request,
|
serviceImpl.streamEvents((mxaccess_gateway.v1.MxaccessGateway.StreamEventsRequest) request,
|
||||||
(io.grpc.stub.StreamObserver<mxaccess_gateway.v1.MxaccessGateway.MxEvent>) responseObserver);
|
(io.grpc.stub.StreamObserver<mxaccess_gateway.v1.MxaccessGateway.MxEvent>) responseObserver);
|
||||||
break;
|
break;
|
||||||
|
case METHODID_ACKNOWLEDGE_ALARM:
|
||||||
|
serviceImpl.acknowledgeAlarm((mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmRequest) request,
|
||||||
|
(io.grpc.stub.StreamObserver<mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmReply>) responseObserver);
|
||||||
|
break;
|
||||||
|
case METHODID_STREAM_ALARMS:
|
||||||
|
serviceImpl.streamAlarms((mxaccess_gateway.v1.MxaccessGateway.StreamAlarmsRequest) request,
|
||||||
|
(io.grpc.stub.StreamObserver<mxaccess_gateway.v1.MxaccessGateway.AlarmFeedMessage>) responseObserver);
|
||||||
|
break;
|
||||||
|
case METHODID_QUERY_ACTIVE_ALARMS:
|
||||||
|
serviceImpl.queryActiveAlarms((mxaccess_gateway.v1.MxaccessGateway.QueryActiveAlarmsRequest) request,
|
||||||
|
(io.grpc.stub.StreamObserver<mxaccess_gateway.v1.MxaccessGateway.ActiveAlarmSnapshot>) responseObserver);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new AssertionError();
|
throw new AssertionError();
|
||||||
}
|
}
|
||||||
@@ -527,6 +804,27 @@ public final class MxAccessGatewayGrpc {
|
|||||||
mxaccess_gateway.v1.MxaccessGateway.StreamEventsRequest,
|
mxaccess_gateway.v1.MxaccessGateway.StreamEventsRequest,
|
||||||
mxaccess_gateway.v1.MxaccessGateway.MxEvent>(
|
mxaccess_gateway.v1.MxaccessGateway.MxEvent>(
|
||||||
service, METHODID_STREAM_EVENTS)))
|
service, METHODID_STREAM_EVENTS)))
|
||||||
|
.addMethod(
|
||||||
|
getAcknowledgeAlarmMethod(),
|
||||||
|
io.grpc.stub.ServerCalls.asyncUnaryCall(
|
||||||
|
new MethodHandlers<
|
||||||
|
mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmRequest,
|
||||||
|
mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmReply>(
|
||||||
|
service, METHODID_ACKNOWLEDGE_ALARM)))
|
||||||
|
.addMethod(
|
||||||
|
getStreamAlarmsMethod(),
|
||||||
|
io.grpc.stub.ServerCalls.asyncServerStreamingCall(
|
||||||
|
new MethodHandlers<
|
||||||
|
mxaccess_gateway.v1.MxaccessGateway.StreamAlarmsRequest,
|
||||||
|
mxaccess_gateway.v1.MxaccessGateway.AlarmFeedMessage>(
|
||||||
|
service, METHODID_STREAM_ALARMS)))
|
||||||
|
.addMethod(
|
||||||
|
getQueryActiveAlarmsMethod(),
|
||||||
|
io.grpc.stub.ServerCalls.asyncServerStreamingCall(
|
||||||
|
new MethodHandlers<
|
||||||
|
mxaccess_gateway.v1.MxaccessGateway.QueryActiveAlarmsRequest,
|
||||||
|
mxaccess_gateway.v1.MxaccessGateway.ActiveAlarmSnapshot>(
|
||||||
|
service, METHODID_QUERY_ACTIVE_ALARMS)))
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -579,6 +877,9 @@ public final class MxAccessGatewayGrpc {
|
|||||||
.addMethod(getCloseSessionMethod())
|
.addMethod(getCloseSessionMethod())
|
||||||
.addMethod(getInvokeMethod())
|
.addMethod(getInvokeMethod())
|
||||||
.addMethod(getStreamEventsMethod())
|
.addMethod(getStreamEventsMethod())
|
||||||
|
.addMethod(getAcknowledgeAlarmMethod())
|
||||||
|
.addMethod(getStreamAlarmsMethod())
|
||||||
|
.addMethod(getQueryActiveAlarmsMethod())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+214
-64
@@ -1750,7 +1750,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
* <code>.google.protobuf.Timestamp time_of_last_deploy = 2;</code>
|
* <code>.google.protobuf.Timestamp time_of_last_deploy = 2;</code>
|
||||||
*/
|
*/
|
||||||
private com.google.protobuf.SingleFieldBuilder<
|
private com.google.protobuf.SingleFieldBuilder<
|
||||||
com.google.protobuf.Timestamp, com.google.protobuf.Timestamp.Builder, com.google.protobuf.TimestampOrBuilder>
|
com.google.protobuf.Timestamp, com.google.protobuf.Timestamp.Builder, com.google.protobuf.TimestampOrBuilder>
|
||||||
internalGetTimeOfLastDeployFieldBuilder() {
|
internalGetTimeOfLastDeployFieldBuilder() {
|
||||||
if (timeOfLastDeployBuilder_ == null) {
|
if (timeOfLastDeployBuilder_ == null) {
|
||||||
timeOfLastDeployBuilder_ = new com.google.protobuf.SingleFieldBuilder<
|
timeOfLastDeployBuilder_ = new com.google.protobuf.SingleFieldBuilder<
|
||||||
@@ -2175,7 +2175,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
if (ref instanceof java.lang.String) {
|
if (ref instanceof java.lang.String) {
|
||||||
return (java.lang.String) ref;
|
return (java.lang.String) ref;
|
||||||
} else {
|
} else {
|
||||||
com.google.protobuf.ByteString bs =
|
com.google.protobuf.ByteString bs =
|
||||||
(com.google.protobuf.ByteString) ref;
|
(com.google.protobuf.ByteString) ref;
|
||||||
java.lang.String s = bs.toStringUtf8();
|
java.lang.String s = bs.toStringUtf8();
|
||||||
pageToken_ = s;
|
pageToken_ = s;
|
||||||
@@ -2195,7 +2195,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
getPageTokenBytes() {
|
getPageTokenBytes() {
|
||||||
java.lang.Object ref = pageToken_;
|
java.lang.Object ref = pageToken_;
|
||||||
if (ref instanceof java.lang.String) {
|
if (ref instanceof java.lang.String) {
|
||||||
com.google.protobuf.ByteString b =
|
com.google.protobuf.ByteString b =
|
||||||
com.google.protobuf.ByteString.copyFromUtf8(
|
com.google.protobuf.ByteString.copyFromUtf8(
|
||||||
(java.lang.String) ref);
|
(java.lang.String) ref);
|
||||||
pageToken_ = b;
|
pageToken_ = b;
|
||||||
@@ -2246,7 +2246,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
if (ref instanceof java.lang.String) {
|
if (ref instanceof java.lang.String) {
|
||||||
return (java.lang.String) ref;
|
return (java.lang.String) ref;
|
||||||
} else {
|
} else {
|
||||||
com.google.protobuf.ByteString bs =
|
com.google.protobuf.ByteString bs =
|
||||||
(com.google.protobuf.ByteString) ref;
|
(com.google.protobuf.ByteString) ref;
|
||||||
java.lang.String s = bs.toStringUtf8();
|
java.lang.String s = bs.toStringUtf8();
|
||||||
if (rootCase_ == 4) {
|
if (rootCase_ == 4) {
|
||||||
@@ -2266,7 +2266,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
ref = root_;
|
ref = root_;
|
||||||
}
|
}
|
||||||
if (ref instanceof java.lang.String) {
|
if (ref instanceof java.lang.String) {
|
||||||
com.google.protobuf.ByteString b =
|
com.google.protobuf.ByteString b =
|
||||||
com.google.protobuf.ByteString.copyFromUtf8(
|
com.google.protobuf.ByteString.copyFromUtf8(
|
||||||
(java.lang.String) ref);
|
(java.lang.String) ref);
|
||||||
if (rootCase_ == 4) {
|
if (rootCase_ == 4) {
|
||||||
@@ -2298,7 +2298,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
if (ref instanceof java.lang.String) {
|
if (ref instanceof java.lang.String) {
|
||||||
return (java.lang.String) ref;
|
return (java.lang.String) ref;
|
||||||
} else {
|
} else {
|
||||||
com.google.protobuf.ByteString bs =
|
com.google.protobuf.ByteString bs =
|
||||||
(com.google.protobuf.ByteString) ref;
|
(com.google.protobuf.ByteString) ref;
|
||||||
java.lang.String s = bs.toStringUtf8();
|
java.lang.String s = bs.toStringUtf8();
|
||||||
if (rootCase_ == 5) {
|
if (rootCase_ == 5) {
|
||||||
@@ -2318,7 +2318,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
ref = root_;
|
ref = root_;
|
||||||
}
|
}
|
||||||
if (ref instanceof java.lang.String) {
|
if (ref instanceof java.lang.String) {
|
||||||
com.google.protobuf.ByteString b =
|
com.google.protobuf.ByteString b =
|
||||||
com.google.protobuf.ByteString.copyFromUtf8(
|
com.google.protobuf.ByteString.copyFromUtf8(
|
||||||
(java.lang.String) ref);
|
(java.lang.String) ref);
|
||||||
if (rootCase_ == 5) {
|
if (rootCase_ == 5) {
|
||||||
@@ -2483,7 +2483,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
if (ref instanceof java.lang.String) {
|
if (ref instanceof java.lang.String) {
|
||||||
return (java.lang.String) ref;
|
return (java.lang.String) ref;
|
||||||
} else {
|
} else {
|
||||||
com.google.protobuf.ByteString bs =
|
com.google.protobuf.ByteString bs =
|
||||||
(com.google.protobuf.ByteString) ref;
|
(com.google.protobuf.ByteString) ref;
|
||||||
java.lang.String s = bs.toStringUtf8();
|
java.lang.String s = bs.toStringUtf8();
|
||||||
tagNameGlob_ = s;
|
tagNameGlob_ = s;
|
||||||
@@ -2503,7 +2503,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
getTagNameGlobBytes() {
|
getTagNameGlobBytes() {
|
||||||
java.lang.Object ref = tagNameGlob_;
|
java.lang.Object ref = tagNameGlob_;
|
||||||
if (ref instanceof java.lang.String) {
|
if (ref instanceof java.lang.String) {
|
||||||
com.google.protobuf.ByteString b =
|
com.google.protobuf.ByteString b =
|
||||||
com.google.protobuf.ByteString.copyFromUtf8(
|
com.google.protobuf.ByteString.copyFromUtf8(
|
||||||
(java.lang.String) ref);
|
(java.lang.String) ref);
|
||||||
tagNameGlob_ = b;
|
tagNameGlob_ = b;
|
||||||
@@ -3328,7 +3328,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
getPageTokenBytes() {
|
getPageTokenBytes() {
|
||||||
java.lang.Object ref = pageToken_;
|
java.lang.Object ref = pageToken_;
|
||||||
if (ref instanceof String) {
|
if (ref instanceof String) {
|
||||||
com.google.protobuf.ByteString b =
|
com.google.protobuf.ByteString b =
|
||||||
com.google.protobuf.ByteString.copyFromUtf8(
|
com.google.protobuf.ByteString.copyFromUtf8(
|
||||||
(java.lang.String) ref);
|
(java.lang.String) ref);
|
||||||
pageToken_ = b;
|
pageToken_ = b;
|
||||||
@@ -3471,7 +3471,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
ref = root_;
|
ref = root_;
|
||||||
}
|
}
|
||||||
if (ref instanceof String) {
|
if (ref instanceof String) {
|
||||||
com.google.protobuf.ByteString b =
|
com.google.protobuf.ByteString b =
|
||||||
com.google.protobuf.ByteString.copyFromUtf8(
|
com.google.protobuf.ByteString.copyFromUtf8(
|
||||||
(java.lang.String) ref);
|
(java.lang.String) ref);
|
||||||
if (rootCase_ == 4) {
|
if (rootCase_ == 4) {
|
||||||
@@ -3564,7 +3564,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
ref = root_;
|
ref = root_;
|
||||||
}
|
}
|
||||||
if (ref instanceof String) {
|
if (ref instanceof String) {
|
||||||
com.google.protobuf.ByteString b =
|
com.google.protobuf.ByteString b =
|
||||||
com.google.protobuf.ByteString.copyFromUtf8(
|
com.google.protobuf.ByteString.copyFromUtf8(
|
||||||
(java.lang.String) ref);
|
(java.lang.String) ref);
|
||||||
if (rootCase_ == 5) {
|
if (rootCase_ == 5) {
|
||||||
@@ -3768,7 +3768,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
* <code>.google.protobuf.Int32Value max_depth = 6;</code>
|
* <code>.google.protobuf.Int32Value max_depth = 6;</code>
|
||||||
*/
|
*/
|
||||||
private com.google.protobuf.SingleFieldBuilder<
|
private com.google.protobuf.SingleFieldBuilder<
|
||||||
com.google.protobuf.Int32Value, com.google.protobuf.Int32Value.Builder, com.google.protobuf.Int32ValueOrBuilder>
|
com.google.protobuf.Int32Value, com.google.protobuf.Int32Value.Builder, com.google.protobuf.Int32ValueOrBuilder>
|
||||||
internalGetMaxDepthFieldBuilder() {
|
internalGetMaxDepthFieldBuilder() {
|
||||||
if (maxDepthBuilder_ == null) {
|
if (maxDepthBuilder_ == null) {
|
||||||
maxDepthBuilder_ = new com.google.protobuf.SingleFieldBuilder<
|
maxDepthBuilder_ = new com.google.protobuf.SingleFieldBuilder<
|
||||||
@@ -4073,7 +4073,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
getTagNameGlobBytes() {
|
getTagNameGlobBytes() {
|
||||||
java.lang.Object ref = tagNameGlob_;
|
java.lang.Object ref = tagNameGlob_;
|
||||||
if (ref instanceof String) {
|
if (ref instanceof String) {
|
||||||
com.google.protobuf.ByteString b =
|
com.google.protobuf.ByteString b =
|
||||||
com.google.protobuf.ByteString.copyFromUtf8(
|
com.google.protobuf.ByteString.copyFromUtf8(
|
||||||
(java.lang.String) ref);
|
(java.lang.String) ref);
|
||||||
tagNameGlob_ = b;
|
tagNameGlob_ = b;
|
||||||
@@ -4334,7 +4334,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
/**
|
/**
|
||||||
* <code>repeated .galaxy_repository.v1.GalaxyObject objects = 1;</code>
|
* <code>repeated .galaxy_repository.v1.GalaxyObject objects = 1;</code>
|
||||||
*/
|
*/
|
||||||
java.util.List<galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyObject>
|
java.util.List<galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyObject>
|
||||||
getObjectsList();
|
getObjectsList();
|
||||||
/**
|
/**
|
||||||
* <code>repeated .galaxy_repository.v1.GalaxyObject objects = 1;</code>
|
* <code>repeated .galaxy_repository.v1.GalaxyObject objects = 1;</code>
|
||||||
@@ -4347,7 +4347,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
/**
|
/**
|
||||||
* <code>repeated .galaxy_repository.v1.GalaxyObject objects = 1;</code>
|
* <code>repeated .galaxy_repository.v1.GalaxyObject objects = 1;</code>
|
||||||
*/
|
*/
|
||||||
java.util.List<? extends galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyObjectOrBuilder>
|
java.util.List<? extends galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyObjectOrBuilder>
|
||||||
getObjectsOrBuilderList();
|
getObjectsOrBuilderList();
|
||||||
/**
|
/**
|
||||||
* <code>repeated .galaxy_repository.v1.GalaxyObject objects = 1;</code>
|
* <code>repeated .galaxy_repository.v1.GalaxyObject objects = 1;</code>
|
||||||
@@ -4438,7 +4438,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
* <code>repeated .galaxy_repository.v1.GalaxyObject objects = 1;</code>
|
* <code>repeated .galaxy_repository.v1.GalaxyObject objects = 1;</code>
|
||||||
*/
|
*/
|
||||||
@java.lang.Override
|
@java.lang.Override
|
||||||
public java.util.List<? extends galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyObjectOrBuilder>
|
public java.util.List<? extends galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyObjectOrBuilder>
|
||||||
getObjectsOrBuilderList() {
|
getObjectsOrBuilderList() {
|
||||||
return objects_;
|
return objects_;
|
||||||
}
|
}
|
||||||
@@ -4482,7 +4482,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
if (ref instanceof java.lang.String) {
|
if (ref instanceof java.lang.String) {
|
||||||
return (java.lang.String) ref;
|
return (java.lang.String) ref;
|
||||||
} else {
|
} else {
|
||||||
com.google.protobuf.ByteString bs =
|
com.google.protobuf.ByteString bs =
|
||||||
(com.google.protobuf.ByteString) ref;
|
(com.google.protobuf.ByteString) ref;
|
||||||
java.lang.String s = bs.toStringUtf8();
|
java.lang.String s = bs.toStringUtf8();
|
||||||
nextPageToken_ = s;
|
nextPageToken_ = s;
|
||||||
@@ -4502,7 +4502,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
getNextPageTokenBytes() {
|
getNextPageTokenBytes() {
|
||||||
java.lang.Object ref = nextPageToken_;
|
java.lang.Object ref = nextPageToken_;
|
||||||
if (ref instanceof java.lang.String) {
|
if (ref instanceof java.lang.String) {
|
||||||
com.google.protobuf.ByteString b =
|
com.google.protobuf.ByteString b =
|
||||||
com.google.protobuf.ByteString.copyFromUtf8(
|
com.google.protobuf.ByteString.copyFromUtf8(
|
||||||
(java.lang.String) ref);
|
(java.lang.String) ref);
|
||||||
nextPageToken_ = b;
|
nextPageToken_ = b;
|
||||||
@@ -4834,7 +4834,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
objectsBuilder_ = null;
|
objectsBuilder_ = null;
|
||||||
objects_ = other.objects_;
|
objects_ = other.objects_;
|
||||||
bitField0_ = (bitField0_ & ~0x00000001);
|
bitField0_ = (bitField0_ & ~0x00000001);
|
||||||
objectsBuilder_ =
|
objectsBuilder_ =
|
||||||
com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ?
|
com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ?
|
||||||
internalGetObjectsFieldBuilder() : null;
|
internalGetObjectsFieldBuilder() : null;
|
||||||
} else {
|
} else {
|
||||||
@@ -5111,7 +5111,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
/**
|
/**
|
||||||
* <code>repeated .galaxy_repository.v1.GalaxyObject objects = 1;</code>
|
* <code>repeated .galaxy_repository.v1.GalaxyObject objects = 1;</code>
|
||||||
*/
|
*/
|
||||||
public java.util.List<? extends galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyObjectOrBuilder>
|
public java.util.List<? extends galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyObjectOrBuilder>
|
||||||
getObjectsOrBuilderList() {
|
getObjectsOrBuilderList() {
|
||||||
if (objectsBuilder_ != null) {
|
if (objectsBuilder_ != null) {
|
||||||
return objectsBuilder_.getMessageOrBuilderList();
|
return objectsBuilder_.getMessageOrBuilderList();
|
||||||
@@ -5137,12 +5137,12 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
/**
|
/**
|
||||||
* <code>repeated .galaxy_repository.v1.GalaxyObject objects = 1;</code>
|
* <code>repeated .galaxy_repository.v1.GalaxyObject objects = 1;</code>
|
||||||
*/
|
*/
|
||||||
public java.util.List<galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyObject.Builder>
|
public java.util.List<galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyObject.Builder>
|
||||||
getObjectsBuilderList() {
|
getObjectsBuilderList() {
|
||||||
return internalGetObjectsFieldBuilder().getBuilderList();
|
return internalGetObjectsFieldBuilder().getBuilderList();
|
||||||
}
|
}
|
||||||
private com.google.protobuf.RepeatedFieldBuilder<
|
private com.google.protobuf.RepeatedFieldBuilder<
|
||||||
galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyObject, galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyObject.Builder, galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyObjectOrBuilder>
|
galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyObject, galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyObject.Builder, galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyObjectOrBuilder>
|
||||||
internalGetObjectsFieldBuilder() {
|
internalGetObjectsFieldBuilder() {
|
||||||
if (objectsBuilder_ == null) {
|
if (objectsBuilder_ == null) {
|
||||||
objectsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder<
|
objectsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder<
|
||||||
@@ -5189,7 +5189,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
getNextPageTokenBytes() {
|
getNextPageTokenBytes() {
|
||||||
java.lang.Object ref = nextPageToken_;
|
java.lang.Object ref = nextPageToken_;
|
||||||
if (ref instanceof String) {
|
if (ref instanceof String) {
|
||||||
com.google.protobuf.ByteString b =
|
com.google.protobuf.ByteString b =
|
||||||
com.google.protobuf.ByteString.copyFromUtf8(
|
com.google.protobuf.ByteString.copyFromUtf8(
|
||||||
(java.lang.String) ref);
|
(java.lang.String) ref);
|
||||||
nextPageToken_ = b;
|
nextPageToken_ = b;
|
||||||
@@ -5924,7 +5924,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
* <code>.google.protobuf.Timestamp last_seen_deploy_time = 1;</code>
|
* <code>.google.protobuf.Timestamp last_seen_deploy_time = 1;</code>
|
||||||
*/
|
*/
|
||||||
private com.google.protobuf.SingleFieldBuilder<
|
private com.google.protobuf.SingleFieldBuilder<
|
||||||
com.google.protobuf.Timestamp, com.google.protobuf.Timestamp.Builder, com.google.protobuf.TimestampOrBuilder>
|
com.google.protobuf.Timestamp, com.google.protobuf.Timestamp.Builder, com.google.protobuf.TimestampOrBuilder>
|
||||||
internalGetLastSeenDeployTimeFieldBuilder() {
|
internalGetLastSeenDeployTimeFieldBuilder() {
|
||||||
if (lastSeenDeployTimeBuilder_ == null) {
|
if (lastSeenDeployTimeBuilder_ == null) {
|
||||||
lastSeenDeployTimeBuilder_ = new com.google.protobuf.SingleFieldBuilder<
|
lastSeenDeployTimeBuilder_ = new com.google.protobuf.SingleFieldBuilder<
|
||||||
@@ -6871,7 +6871,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
* <code>.google.protobuf.Timestamp observed_at = 2;</code>
|
* <code>.google.protobuf.Timestamp observed_at = 2;</code>
|
||||||
*/
|
*/
|
||||||
private com.google.protobuf.SingleFieldBuilder<
|
private com.google.protobuf.SingleFieldBuilder<
|
||||||
com.google.protobuf.Timestamp, com.google.protobuf.Timestamp.Builder, com.google.protobuf.TimestampOrBuilder>
|
com.google.protobuf.Timestamp, com.google.protobuf.Timestamp.Builder, com.google.protobuf.TimestampOrBuilder>
|
||||||
internalGetObservedAtFieldBuilder() {
|
internalGetObservedAtFieldBuilder() {
|
||||||
if (observedAtBuilder_ == null) {
|
if (observedAtBuilder_ == null) {
|
||||||
observedAtBuilder_ = new com.google.protobuf.SingleFieldBuilder<
|
observedAtBuilder_ = new com.google.protobuf.SingleFieldBuilder<
|
||||||
@@ -7028,7 +7028,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
* <code>.google.protobuf.Timestamp time_of_last_deploy = 3;</code>
|
* <code>.google.protobuf.Timestamp time_of_last_deploy = 3;</code>
|
||||||
*/
|
*/
|
||||||
private com.google.protobuf.SingleFieldBuilder<
|
private com.google.protobuf.SingleFieldBuilder<
|
||||||
com.google.protobuf.Timestamp, com.google.protobuf.Timestamp.Builder, com.google.protobuf.TimestampOrBuilder>
|
com.google.protobuf.Timestamp, com.google.protobuf.Timestamp.Builder, com.google.protobuf.TimestampOrBuilder>
|
||||||
internalGetTimeOfLastDeployFieldBuilder() {
|
internalGetTimeOfLastDeployFieldBuilder() {
|
||||||
if (timeOfLastDeployBuilder_ == null) {
|
if (timeOfLastDeployBuilder_ == null) {
|
||||||
timeOfLastDeployBuilder_ = new com.google.protobuf.SingleFieldBuilder<
|
timeOfLastDeployBuilder_ = new com.google.protobuf.SingleFieldBuilder<
|
||||||
@@ -7286,7 +7286,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
/**
|
/**
|
||||||
* <code>repeated .galaxy_repository.v1.GalaxyAttribute attributes = 10;</code>
|
* <code>repeated .galaxy_repository.v1.GalaxyAttribute attributes = 10;</code>
|
||||||
*/
|
*/
|
||||||
java.util.List<galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyAttribute>
|
java.util.List<galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyAttribute>
|
||||||
getAttributesList();
|
getAttributesList();
|
||||||
/**
|
/**
|
||||||
* <code>repeated .galaxy_repository.v1.GalaxyAttribute attributes = 10;</code>
|
* <code>repeated .galaxy_repository.v1.GalaxyAttribute attributes = 10;</code>
|
||||||
@@ -7299,7 +7299,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
/**
|
/**
|
||||||
* <code>repeated .galaxy_repository.v1.GalaxyAttribute attributes = 10;</code>
|
* <code>repeated .galaxy_repository.v1.GalaxyAttribute attributes = 10;</code>
|
||||||
*/
|
*/
|
||||||
java.util.List<? extends galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyAttributeOrBuilder>
|
java.util.List<? extends galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyAttributeOrBuilder>
|
||||||
getAttributesOrBuilderList();
|
getAttributesOrBuilderList();
|
||||||
/**
|
/**
|
||||||
* <code>repeated .galaxy_repository.v1.GalaxyAttribute attributes = 10;</code>
|
* <code>repeated .galaxy_repository.v1.GalaxyAttribute attributes = 10;</code>
|
||||||
@@ -7374,7 +7374,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
if (ref instanceof java.lang.String) {
|
if (ref instanceof java.lang.String) {
|
||||||
return (java.lang.String) ref;
|
return (java.lang.String) ref;
|
||||||
} else {
|
} else {
|
||||||
com.google.protobuf.ByteString bs =
|
com.google.protobuf.ByteString bs =
|
||||||
(com.google.protobuf.ByteString) ref;
|
(com.google.protobuf.ByteString) ref;
|
||||||
java.lang.String s = bs.toStringUtf8();
|
java.lang.String s = bs.toStringUtf8();
|
||||||
tagName_ = s;
|
tagName_ = s;
|
||||||
@@ -7390,7 +7390,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
getTagNameBytes() {
|
getTagNameBytes() {
|
||||||
java.lang.Object ref = tagName_;
|
java.lang.Object ref = tagName_;
|
||||||
if (ref instanceof java.lang.String) {
|
if (ref instanceof java.lang.String) {
|
||||||
com.google.protobuf.ByteString b =
|
com.google.protobuf.ByteString b =
|
||||||
com.google.protobuf.ByteString.copyFromUtf8(
|
com.google.protobuf.ByteString.copyFromUtf8(
|
||||||
(java.lang.String) ref);
|
(java.lang.String) ref);
|
||||||
tagName_ = b;
|
tagName_ = b;
|
||||||
@@ -7413,7 +7413,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
if (ref instanceof java.lang.String) {
|
if (ref instanceof java.lang.String) {
|
||||||
return (java.lang.String) ref;
|
return (java.lang.String) ref;
|
||||||
} else {
|
} else {
|
||||||
com.google.protobuf.ByteString bs =
|
com.google.protobuf.ByteString bs =
|
||||||
(com.google.protobuf.ByteString) ref;
|
(com.google.protobuf.ByteString) ref;
|
||||||
java.lang.String s = bs.toStringUtf8();
|
java.lang.String s = bs.toStringUtf8();
|
||||||
containedName_ = s;
|
containedName_ = s;
|
||||||
@@ -7429,7 +7429,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
getContainedNameBytes() {
|
getContainedNameBytes() {
|
||||||
java.lang.Object ref = containedName_;
|
java.lang.Object ref = containedName_;
|
||||||
if (ref instanceof java.lang.String) {
|
if (ref instanceof java.lang.String) {
|
||||||
com.google.protobuf.ByteString b =
|
com.google.protobuf.ByteString b =
|
||||||
com.google.protobuf.ByteString.copyFromUtf8(
|
com.google.protobuf.ByteString.copyFromUtf8(
|
||||||
(java.lang.String) ref);
|
(java.lang.String) ref);
|
||||||
containedName_ = b;
|
containedName_ = b;
|
||||||
@@ -7452,7 +7452,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
if (ref instanceof java.lang.String) {
|
if (ref instanceof java.lang.String) {
|
||||||
return (java.lang.String) ref;
|
return (java.lang.String) ref;
|
||||||
} else {
|
} else {
|
||||||
com.google.protobuf.ByteString bs =
|
com.google.protobuf.ByteString bs =
|
||||||
(com.google.protobuf.ByteString) ref;
|
(com.google.protobuf.ByteString) ref;
|
||||||
java.lang.String s = bs.toStringUtf8();
|
java.lang.String s = bs.toStringUtf8();
|
||||||
browseName_ = s;
|
browseName_ = s;
|
||||||
@@ -7468,7 +7468,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
getBrowseNameBytes() {
|
getBrowseNameBytes() {
|
||||||
java.lang.Object ref = browseName_;
|
java.lang.Object ref = browseName_;
|
||||||
if (ref instanceof java.lang.String) {
|
if (ref instanceof java.lang.String) {
|
||||||
com.google.protobuf.ByteString b =
|
com.google.protobuf.ByteString b =
|
||||||
com.google.protobuf.ByteString.copyFromUtf8(
|
com.google.protobuf.ByteString.copyFromUtf8(
|
||||||
(java.lang.String) ref);
|
(java.lang.String) ref);
|
||||||
browseName_ = b;
|
browseName_ = b;
|
||||||
@@ -7573,7 +7573,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
* <code>repeated .galaxy_repository.v1.GalaxyAttribute attributes = 10;</code>
|
* <code>repeated .galaxy_repository.v1.GalaxyAttribute attributes = 10;</code>
|
||||||
*/
|
*/
|
||||||
@java.lang.Override
|
@java.lang.Override
|
||||||
public java.util.List<? extends galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyAttributeOrBuilder>
|
public java.util.List<? extends galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyAttributeOrBuilder>
|
||||||
getAttributesOrBuilderList() {
|
getAttributesOrBuilderList() {
|
||||||
return attributes_;
|
return attributes_;
|
||||||
}
|
}
|
||||||
@@ -8059,7 +8059,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
attributesBuilder_ = null;
|
attributesBuilder_ = null;
|
||||||
attributes_ = other.attributes_;
|
attributes_ = other.attributes_;
|
||||||
bitField0_ = (bitField0_ & ~0x00000200);
|
bitField0_ = (bitField0_ & ~0x00000200);
|
||||||
attributesBuilder_ =
|
attributesBuilder_ =
|
||||||
com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ?
|
com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ?
|
||||||
internalGetAttributesFieldBuilder() : null;
|
internalGetAttributesFieldBuilder() : null;
|
||||||
} else {
|
} else {
|
||||||
@@ -8226,7 +8226,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
getTagNameBytes() {
|
getTagNameBytes() {
|
||||||
java.lang.Object ref = tagName_;
|
java.lang.Object ref = tagName_;
|
||||||
if (ref instanceof String) {
|
if (ref instanceof String) {
|
||||||
com.google.protobuf.ByteString b =
|
com.google.protobuf.ByteString b =
|
||||||
com.google.protobuf.ByteString.copyFromUtf8(
|
com.google.protobuf.ByteString.copyFromUtf8(
|
||||||
(java.lang.String) ref);
|
(java.lang.String) ref);
|
||||||
tagName_ = b;
|
tagName_ = b;
|
||||||
@@ -8298,7 +8298,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
getContainedNameBytes() {
|
getContainedNameBytes() {
|
||||||
java.lang.Object ref = containedName_;
|
java.lang.Object ref = containedName_;
|
||||||
if (ref instanceof String) {
|
if (ref instanceof String) {
|
||||||
com.google.protobuf.ByteString b =
|
com.google.protobuf.ByteString b =
|
||||||
com.google.protobuf.ByteString.copyFromUtf8(
|
com.google.protobuf.ByteString.copyFromUtf8(
|
||||||
(java.lang.String) ref);
|
(java.lang.String) ref);
|
||||||
containedName_ = b;
|
containedName_ = b;
|
||||||
@@ -8370,7 +8370,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
getBrowseNameBytes() {
|
getBrowseNameBytes() {
|
||||||
java.lang.Object ref = browseName_;
|
java.lang.Object ref = browseName_;
|
||||||
if (ref instanceof String) {
|
if (ref instanceof String) {
|
||||||
com.google.protobuf.ByteString b =
|
com.google.protobuf.ByteString b =
|
||||||
com.google.protobuf.ByteString.copyFromUtf8(
|
com.google.protobuf.ByteString.copyFromUtf8(
|
||||||
(java.lang.String) ref);
|
(java.lang.String) ref);
|
||||||
browseName_ = b;
|
browseName_ = b;
|
||||||
@@ -8851,7 +8851,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
/**
|
/**
|
||||||
* <code>repeated .galaxy_repository.v1.GalaxyAttribute attributes = 10;</code>
|
* <code>repeated .galaxy_repository.v1.GalaxyAttribute attributes = 10;</code>
|
||||||
*/
|
*/
|
||||||
public java.util.List<? extends galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyAttributeOrBuilder>
|
public java.util.List<? extends galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyAttributeOrBuilder>
|
||||||
getAttributesOrBuilderList() {
|
getAttributesOrBuilderList() {
|
||||||
if (attributesBuilder_ != null) {
|
if (attributesBuilder_ != null) {
|
||||||
return attributesBuilder_.getMessageOrBuilderList();
|
return attributesBuilder_.getMessageOrBuilderList();
|
||||||
@@ -8877,12 +8877,12 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
/**
|
/**
|
||||||
* <code>repeated .galaxy_repository.v1.GalaxyAttribute attributes = 10;</code>
|
* <code>repeated .galaxy_repository.v1.GalaxyAttribute attributes = 10;</code>
|
||||||
*/
|
*/
|
||||||
public java.util.List<galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyAttribute.Builder>
|
public java.util.List<galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyAttribute.Builder>
|
||||||
getAttributesBuilderList() {
|
getAttributesBuilderList() {
|
||||||
return internalGetAttributesFieldBuilder().getBuilderList();
|
return internalGetAttributesFieldBuilder().getBuilderList();
|
||||||
}
|
}
|
||||||
private com.google.protobuf.RepeatedFieldBuilder<
|
private com.google.protobuf.RepeatedFieldBuilder<
|
||||||
galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyAttribute, galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyAttribute.Builder, galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyAttributeOrBuilder>
|
galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyAttribute, galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyAttribute.Builder, galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyAttributeOrBuilder>
|
||||||
internalGetAttributesFieldBuilder() {
|
internalGetAttributesFieldBuilder() {
|
||||||
if (attributesBuilder_ == null) {
|
if (attributesBuilder_ == null) {
|
||||||
attributesBuilder_ = new com.google.protobuf.RepeatedFieldBuilder<
|
attributesBuilder_ = new com.google.protobuf.RepeatedFieldBuilder<
|
||||||
@@ -8976,17 +8976,36 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
getFullTagReferenceBytes();
|
getFullTagReferenceBytes();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Raw Galaxy SQL `dbo.data_type` identifier, passed through unchanged.
|
||||||
|
* This is NOT a member of `mxaccess_gateway.v1.MxDataType` — Galaxy's
|
||||||
|
* type enumeration is distinct from MXAccess's wire data-type enum and
|
||||||
|
* the two must not be cast or compared. The GalaxyRepository service is
|
||||||
|
* metadata-only and deliberately does not share types with
|
||||||
|
* mxaccess_gateway.proto. See docs/GalaxyRepository.md.
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
* <code>int32 mx_data_type = 3;</code>
|
* <code>int32 mx_data_type = 3;</code>
|
||||||
* @return The mxDataType.
|
* @return The mxDataType.
|
||||||
*/
|
*/
|
||||||
int getMxDataType();
|
int getMxDataType();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Human-readable name from Galaxy's `dbo.data_type` table (e.g. "Float",
|
||||||
|
* "Integer", "Boolean"). Free-form Galaxy text; not a stable enum.
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
* <code>string data_type_name = 4;</code>
|
* <code>string data_type_name = 4;</code>
|
||||||
* @return The dataTypeName.
|
* @return The dataTypeName.
|
||||||
*/
|
*/
|
||||||
java.lang.String getDataTypeName();
|
java.lang.String getDataTypeName();
|
||||||
/**
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Human-readable name from Galaxy's `dbo.data_type` table (e.g. "Float",
|
||||||
|
* "Integer", "Boolean"). Free-form Galaxy text; not a stable enum.
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
* <code>string data_type_name = 4;</code>
|
* <code>string data_type_name = 4;</code>
|
||||||
* @return The bytes for dataTypeName.
|
* @return The bytes for dataTypeName.
|
||||||
*/
|
*/
|
||||||
@@ -9012,12 +9031,24 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
boolean getArrayDimensionPresent();
|
boolean getArrayDimensionPresent();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Raw Galaxy SQL attribute-category identifier, passed through unchanged.
|
||||||
|
* Galaxy-specific; not mapped to any gateway enum. See
|
||||||
|
* docs/GalaxyRepository.md.
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
* <code>int32 mx_attribute_category = 8;</code>
|
* <code>int32 mx_attribute_category = 8;</code>
|
||||||
* @return The mxAttributeCategory.
|
* @return The mxAttributeCategory.
|
||||||
*/
|
*/
|
||||||
int getMxAttributeCategory();
|
int getMxAttributeCategory();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Raw Galaxy SQL security-classification identifier, passed through
|
||||||
|
* unchanged. Galaxy-specific; not mapped to any gateway enum. See
|
||||||
|
* docs/GalaxyRepository.md.
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
* <code>int32 security_classification = 9;</code>
|
* <code>int32 security_classification = 9;</code>
|
||||||
* @return The securityClassification.
|
* @return The securityClassification.
|
||||||
*/
|
*/
|
||||||
@@ -9088,7 +9119,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
if (ref instanceof java.lang.String) {
|
if (ref instanceof java.lang.String) {
|
||||||
return (java.lang.String) ref;
|
return (java.lang.String) ref;
|
||||||
} else {
|
} else {
|
||||||
com.google.protobuf.ByteString bs =
|
com.google.protobuf.ByteString bs =
|
||||||
(com.google.protobuf.ByteString) ref;
|
(com.google.protobuf.ByteString) ref;
|
||||||
java.lang.String s = bs.toStringUtf8();
|
java.lang.String s = bs.toStringUtf8();
|
||||||
attributeName_ = s;
|
attributeName_ = s;
|
||||||
@@ -9104,7 +9135,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
getAttributeNameBytes() {
|
getAttributeNameBytes() {
|
||||||
java.lang.Object ref = attributeName_;
|
java.lang.Object ref = attributeName_;
|
||||||
if (ref instanceof java.lang.String) {
|
if (ref instanceof java.lang.String) {
|
||||||
com.google.protobuf.ByteString b =
|
com.google.protobuf.ByteString b =
|
||||||
com.google.protobuf.ByteString.copyFromUtf8(
|
com.google.protobuf.ByteString.copyFromUtf8(
|
||||||
(java.lang.String) ref);
|
(java.lang.String) ref);
|
||||||
attributeName_ = b;
|
attributeName_ = b;
|
||||||
@@ -9127,7 +9158,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
if (ref instanceof java.lang.String) {
|
if (ref instanceof java.lang.String) {
|
||||||
return (java.lang.String) ref;
|
return (java.lang.String) ref;
|
||||||
} else {
|
} else {
|
||||||
com.google.protobuf.ByteString bs =
|
com.google.protobuf.ByteString bs =
|
||||||
(com.google.protobuf.ByteString) ref;
|
(com.google.protobuf.ByteString) ref;
|
||||||
java.lang.String s = bs.toStringUtf8();
|
java.lang.String s = bs.toStringUtf8();
|
||||||
fullTagReference_ = s;
|
fullTagReference_ = s;
|
||||||
@@ -9143,7 +9174,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
getFullTagReferenceBytes() {
|
getFullTagReferenceBytes() {
|
||||||
java.lang.Object ref = fullTagReference_;
|
java.lang.Object ref = fullTagReference_;
|
||||||
if (ref instanceof java.lang.String) {
|
if (ref instanceof java.lang.String) {
|
||||||
com.google.protobuf.ByteString b =
|
com.google.protobuf.ByteString b =
|
||||||
com.google.protobuf.ByteString.copyFromUtf8(
|
com.google.protobuf.ByteString.copyFromUtf8(
|
||||||
(java.lang.String) ref);
|
(java.lang.String) ref);
|
||||||
fullTagReference_ = b;
|
fullTagReference_ = b;
|
||||||
@@ -9156,6 +9187,15 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
public static final int MX_DATA_TYPE_FIELD_NUMBER = 3;
|
public static final int MX_DATA_TYPE_FIELD_NUMBER = 3;
|
||||||
private int mxDataType_ = 0;
|
private int mxDataType_ = 0;
|
||||||
/**
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Raw Galaxy SQL `dbo.data_type` identifier, passed through unchanged.
|
||||||
|
* This is NOT a member of `mxaccess_gateway.v1.MxDataType` — Galaxy's
|
||||||
|
* type enumeration is distinct from MXAccess's wire data-type enum and
|
||||||
|
* the two must not be cast or compared. The GalaxyRepository service is
|
||||||
|
* metadata-only and deliberately does not share types with
|
||||||
|
* mxaccess_gateway.proto. See docs/GalaxyRepository.md.
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
* <code>int32 mx_data_type = 3;</code>
|
* <code>int32 mx_data_type = 3;</code>
|
||||||
* @return The mxDataType.
|
* @return The mxDataType.
|
||||||
*/
|
*/
|
||||||
@@ -9168,6 +9208,11 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
private volatile java.lang.Object dataTypeName_ = "";
|
private volatile java.lang.Object dataTypeName_ = "";
|
||||||
/**
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Human-readable name from Galaxy's `dbo.data_type` table (e.g. "Float",
|
||||||
|
* "Integer", "Boolean"). Free-form Galaxy text; not a stable enum.
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
* <code>string data_type_name = 4;</code>
|
* <code>string data_type_name = 4;</code>
|
||||||
* @return The dataTypeName.
|
* @return The dataTypeName.
|
||||||
*/
|
*/
|
||||||
@@ -9177,7 +9222,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
if (ref instanceof java.lang.String) {
|
if (ref instanceof java.lang.String) {
|
||||||
return (java.lang.String) ref;
|
return (java.lang.String) ref;
|
||||||
} else {
|
} else {
|
||||||
com.google.protobuf.ByteString bs =
|
com.google.protobuf.ByteString bs =
|
||||||
(com.google.protobuf.ByteString) ref;
|
(com.google.protobuf.ByteString) ref;
|
||||||
java.lang.String s = bs.toStringUtf8();
|
java.lang.String s = bs.toStringUtf8();
|
||||||
dataTypeName_ = s;
|
dataTypeName_ = s;
|
||||||
@@ -9185,6 +9230,11 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Human-readable name from Galaxy's `dbo.data_type` table (e.g. "Float",
|
||||||
|
* "Integer", "Boolean"). Free-form Galaxy text; not a stable enum.
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
* <code>string data_type_name = 4;</code>
|
* <code>string data_type_name = 4;</code>
|
||||||
* @return The bytes for dataTypeName.
|
* @return The bytes for dataTypeName.
|
||||||
*/
|
*/
|
||||||
@@ -9193,7 +9243,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
getDataTypeNameBytes() {
|
getDataTypeNameBytes() {
|
||||||
java.lang.Object ref = dataTypeName_;
|
java.lang.Object ref = dataTypeName_;
|
||||||
if (ref instanceof java.lang.String) {
|
if (ref instanceof java.lang.String) {
|
||||||
com.google.protobuf.ByteString b =
|
com.google.protobuf.ByteString b =
|
||||||
com.google.protobuf.ByteString.copyFromUtf8(
|
com.google.protobuf.ByteString.copyFromUtf8(
|
||||||
(java.lang.String) ref);
|
(java.lang.String) ref);
|
||||||
dataTypeName_ = b;
|
dataTypeName_ = b;
|
||||||
@@ -9239,6 +9289,12 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
public static final int MX_ATTRIBUTE_CATEGORY_FIELD_NUMBER = 8;
|
public static final int MX_ATTRIBUTE_CATEGORY_FIELD_NUMBER = 8;
|
||||||
private int mxAttributeCategory_ = 0;
|
private int mxAttributeCategory_ = 0;
|
||||||
/**
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Raw Galaxy SQL attribute-category identifier, passed through unchanged.
|
||||||
|
* Galaxy-specific; not mapped to any gateway enum. See
|
||||||
|
* docs/GalaxyRepository.md.
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
* <code>int32 mx_attribute_category = 8;</code>
|
* <code>int32 mx_attribute_category = 8;</code>
|
||||||
* @return The mxAttributeCategory.
|
* @return The mxAttributeCategory.
|
||||||
*/
|
*/
|
||||||
@@ -9250,6 +9306,12 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
public static final int SECURITY_CLASSIFICATION_FIELD_NUMBER = 9;
|
public static final int SECURITY_CLASSIFICATION_FIELD_NUMBER = 9;
|
||||||
private int securityClassification_ = 0;
|
private int securityClassification_ = 0;
|
||||||
/**
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Raw Galaxy SQL security-classification identifier, passed through
|
||||||
|
* unchanged. Galaxy-specific; not mapped to any gateway enum. See
|
||||||
|
* docs/GalaxyRepository.md.
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
* <code>int32 security_classification = 9;</code>
|
* <code>int32 security_classification = 9;</code>
|
||||||
* @return The securityClassification.
|
* @return The securityClassification.
|
||||||
*/
|
*/
|
||||||
@@ -9835,7 +9897,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
getAttributeNameBytes() {
|
getAttributeNameBytes() {
|
||||||
java.lang.Object ref = attributeName_;
|
java.lang.Object ref = attributeName_;
|
||||||
if (ref instanceof String) {
|
if (ref instanceof String) {
|
||||||
com.google.protobuf.ByteString b =
|
com.google.protobuf.ByteString b =
|
||||||
com.google.protobuf.ByteString.copyFromUtf8(
|
com.google.protobuf.ByteString.copyFromUtf8(
|
||||||
(java.lang.String) ref);
|
(java.lang.String) ref);
|
||||||
attributeName_ = b;
|
attributeName_ = b;
|
||||||
@@ -9907,7 +9969,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
getFullTagReferenceBytes() {
|
getFullTagReferenceBytes() {
|
||||||
java.lang.Object ref = fullTagReference_;
|
java.lang.Object ref = fullTagReference_;
|
||||||
if (ref instanceof String) {
|
if (ref instanceof String) {
|
||||||
com.google.protobuf.ByteString b =
|
com.google.protobuf.ByteString b =
|
||||||
com.google.protobuf.ByteString.copyFromUtf8(
|
com.google.protobuf.ByteString.copyFromUtf8(
|
||||||
(java.lang.String) ref);
|
(java.lang.String) ref);
|
||||||
fullTagReference_ = b;
|
fullTagReference_ = b;
|
||||||
@@ -9956,6 +10018,15 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
|
|
||||||
private int mxDataType_ ;
|
private int mxDataType_ ;
|
||||||
/**
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Raw Galaxy SQL `dbo.data_type` identifier, passed through unchanged.
|
||||||
|
* This is NOT a member of `mxaccess_gateway.v1.MxDataType` — Galaxy's
|
||||||
|
* type enumeration is distinct from MXAccess's wire data-type enum and
|
||||||
|
* the two must not be cast or compared. The GalaxyRepository service is
|
||||||
|
* metadata-only and deliberately does not share types with
|
||||||
|
* mxaccess_gateway.proto. See docs/GalaxyRepository.md.
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
* <code>int32 mx_data_type = 3;</code>
|
* <code>int32 mx_data_type = 3;</code>
|
||||||
* @return The mxDataType.
|
* @return The mxDataType.
|
||||||
*/
|
*/
|
||||||
@@ -9964,6 +10035,15 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
return mxDataType_;
|
return mxDataType_;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Raw Galaxy SQL `dbo.data_type` identifier, passed through unchanged.
|
||||||
|
* This is NOT a member of `mxaccess_gateway.v1.MxDataType` — Galaxy's
|
||||||
|
* type enumeration is distinct from MXAccess's wire data-type enum and
|
||||||
|
* the two must not be cast or compared. The GalaxyRepository service is
|
||||||
|
* metadata-only and deliberately does not share types with
|
||||||
|
* mxaccess_gateway.proto. See docs/GalaxyRepository.md.
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
* <code>int32 mx_data_type = 3;</code>
|
* <code>int32 mx_data_type = 3;</code>
|
||||||
* @param value The mxDataType to set.
|
* @param value The mxDataType to set.
|
||||||
* @return This builder for chaining.
|
* @return This builder for chaining.
|
||||||
@@ -9976,6 +10056,15 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Raw Galaxy SQL `dbo.data_type` identifier, passed through unchanged.
|
||||||
|
* This is NOT a member of `mxaccess_gateway.v1.MxDataType` — Galaxy's
|
||||||
|
* type enumeration is distinct from MXAccess's wire data-type enum and
|
||||||
|
* the two must not be cast or compared. The GalaxyRepository service is
|
||||||
|
* metadata-only and deliberately does not share types with
|
||||||
|
* mxaccess_gateway.proto. See docs/GalaxyRepository.md.
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
* <code>int32 mx_data_type = 3;</code>
|
* <code>int32 mx_data_type = 3;</code>
|
||||||
* @return This builder for chaining.
|
* @return This builder for chaining.
|
||||||
*/
|
*/
|
||||||
@@ -9988,6 +10077,11 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
|
|
||||||
private java.lang.Object dataTypeName_ = "";
|
private java.lang.Object dataTypeName_ = "";
|
||||||
/**
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Human-readable name from Galaxy's `dbo.data_type` table (e.g. "Float",
|
||||||
|
* "Integer", "Boolean"). Free-form Galaxy text; not a stable enum.
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
* <code>string data_type_name = 4;</code>
|
* <code>string data_type_name = 4;</code>
|
||||||
* @return The dataTypeName.
|
* @return The dataTypeName.
|
||||||
*/
|
*/
|
||||||
@@ -10004,6 +10098,11 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Human-readable name from Galaxy's `dbo.data_type` table (e.g. "Float",
|
||||||
|
* "Integer", "Boolean"). Free-form Galaxy text; not a stable enum.
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
* <code>string data_type_name = 4;</code>
|
* <code>string data_type_name = 4;</code>
|
||||||
* @return The bytes for dataTypeName.
|
* @return The bytes for dataTypeName.
|
||||||
*/
|
*/
|
||||||
@@ -10011,7 +10110,7 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
getDataTypeNameBytes() {
|
getDataTypeNameBytes() {
|
||||||
java.lang.Object ref = dataTypeName_;
|
java.lang.Object ref = dataTypeName_;
|
||||||
if (ref instanceof String) {
|
if (ref instanceof String) {
|
||||||
com.google.protobuf.ByteString b =
|
com.google.protobuf.ByteString b =
|
||||||
com.google.protobuf.ByteString.copyFromUtf8(
|
com.google.protobuf.ByteString.copyFromUtf8(
|
||||||
(java.lang.String) ref);
|
(java.lang.String) ref);
|
||||||
dataTypeName_ = b;
|
dataTypeName_ = b;
|
||||||
@@ -10021,6 +10120,11 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Human-readable name from Galaxy's `dbo.data_type` table (e.g. "Float",
|
||||||
|
* "Integer", "Boolean"). Free-form Galaxy text; not a stable enum.
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
* <code>string data_type_name = 4;</code>
|
* <code>string data_type_name = 4;</code>
|
||||||
* @param value The dataTypeName to set.
|
* @param value The dataTypeName to set.
|
||||||
* @return This builder for chaining.
|
* @return This builder for chaining.
|
||||||
@@ -10034,6 +10138,11 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Human-readable name from Galaxy's `dbo.data_type` table (e.g. "Float",
|
||||||
|
* "Integer", "Boolean"). Free-form Galaxy text; not a stable enum.
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
* <code>string data_type_name = 4;</code>
|
* <code>string data_type_name = 4;</code>
|
||||||
* @return This builder for chaining.
|
* @return This builder for chaining.
|
||||||
*/
|
*/
|
||||||
@@ -10044,6 +10153,11 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Human-readable name from Galaxy's `dbo.data_type` table (e.g. "Float",
|
||||||
|
* "Integer", "Boolean"). Free-form Galaxy text; not a stable enum.
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
* <code>string data_type_name = 4;</code>
|
* <code>string data_type_name = 4;</code>
|
||||||
* @param value The bytes for dataTypeName to set.
|
* @param value The bytes for dataTypeName to set.
|
||||||
* @return This builder for chaining.
|
* @return This builder for chaining.
|
||||||
@@ -10156,6 +10270,12 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
|
|
||||||
private int mxAttributeCategory_ ;
|
private int mxAttributeCategory_ ;
|
||||||
/**
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Raw Galaxy SQL attribute-category identifier, passed through unchanged.
|
||||||
|
* Galaxy-specific; not mapped to any gateway enum. See
|
||||||
|
* docs/GalaxyRepository.md.
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
* <code>int32 mx_attribute_category = 8;</code>
|
* <code>int32 mx_attribute_category = 8;</code>
|
||||||
* @return The mxAttributeCategory.
|
* @return The mxAttributeCategory.
|
||||||
*/
|
*/
|
||||||
@@ -10164,6 +10284,12 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
return mxAttributeCategory_;
|
return mxAttributeCategory_;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Raw Galaxy SQL attribute-category identifier, passed through unchanged.
|
||||||
|
* Galaxy-specific; not mapped to any gateway enum. See
|
||||||
|
* docs/GalaxyRepository.md.
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
* <code>int32 mx_attribute_category = 8;</code>
|
* <code>int32 mx_attribute_category = 8;</code>
|
||||||
* @param value The mxAttributeCategory to set.
|
* @param value The mxAttributeCategory to set.
|
||||||
* @return This builder for chaining.
|
* @return This builder for chaining.
|
||||||
@@ -10176,6 +10302,12 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Raw Galaxy SQL attribute-category identifier, passed through unchanged.
|
||||||
|
* Galaxy-specific; not mapped to any gateway enum. See
|
||||||
|
* docs/GalaxyRepository.md.
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
* <code>int32 mx_attribute_category = 8;</code>
|
* <code>int32 mx_attribute_category = 8;</code>
|
||||||
* @return This builder for chaining.
|
* @return This builder for chaining.
|
||||||
*/
|
*/
|
||||||
@@ -10188,6 +10320,12 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
|
|
||||||
private int securityClassification_ ;
|
private int securityClassification_ ;
|
||||||
/**
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Raw Galaxy SQL security-classification identifier, passed through
|
||||||
|
* unchanged. Galaxy-specific; not mapped to any gateway enum. See
|
||||||
|
* docs/GalaxyRepository.md.
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
* <code>int32 security_classification = 9;</code>
|
* <code>int32 security_classification = 9;</code>
|
||||||
* @return The securityClassification.
|
* @return The securityClassification.
|
||||||
*/
|
*/
|
||||||
@@ -10196,6 +10334,12 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
return securityClassification_;
|
return securityClassification_;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Raw Galaxy SQL security-classification identifier, passed through
|
||||||
|
* unchanged. Galaxy-specific; not mapped to any gateway enum. See
|
||||||
|
* docs/GalaxyRepository.md.
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
* <code>int32 security_classification = 9;</code>
|
* <code>int32 security_classification = 9;</code>
|
||||||
* @param value The securityClassification to set.
|
* @param value The securityClassification to set.
|
||||||
* @return This builder for chaining.
|
* @return This builder for chaining.
|
||||||
@@ -10208,6 +10352,12 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Raw Galaxy SQL security-classification identifier, passed through
|
||||||
|
* unchanged. Galaxy-specific; not mapped to any gateway enum. See
|
||||||
|
* docs/GalaxyRepository.md.
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
* <code>int32 security_classification = 9;</code>
|
* <code>int32 security_classification = 9;</code>
|
||||||
* @return This builder for chaining.
|
* @return This builder for chaining.
|
||||||
*/
|
*/
|
||||||
@@ -10335,52 +10485,52 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
|
|
||||||
private static final com.google.protobuf.Descriptors.Descriptor
|
private static final com.google.protobuf.Descriptors.Descriptor
|
||||||
internal_static_galaxy_repository_v1_TestConnectionRequest_descriptor;
|
internal_static_galaxy_repository_v1_TestConnectionRequest_descriptor;
|
||||||
private static final
|
private static final
|
||||||
com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
||||||
internal_static_galaxy_repository_v1_TestConnectionRequest_fieldAccessorTable;
|
internal_static_galaxy_repository_v1_TestConnectionRequest_fieldAccessorTable;
|
||||||
private static final com.google.protobuf.Descriptors.Descriptor
|
private static final com.google.protobuf.Descriptors.Descriptor
|
||||||
internal_static_galaxy_repository_v1_TestConnectionReply_descriptor;
|
internal_static_galaxy_repository_v1_TestConnectionReply_descriptor;
|
||||||
private static final
|
private static final
|
||||||
com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
||||||
internal_static_galaxy_repository_v1_TestConnectionReply_fieldAccessorTable;
|
internal_static_galaxy_repository_v1_TestConnectionReply_fieldAccessorTable;
|
||||||
private static final com.google.protobuf.Descriptors.Descriptor
|
private static final com.google.protobuf.Descriptors.Descriptor
|
||||||
internal_static_galaxy_repository_v1_GetLastDeployTimeRequest_descriptor;
|
internal_static_galaxy_repository_v1_GetLastDeployTimeRequest_descriptor;
|
||||||
private static final
|
private static final
|
||||||
com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
||||||
internal_static_galaxy_repository_v1_GetLastDeployTimeRequest_fieldAccessorTable;
|
internal_static_galaxy_repository_v1_GetLastDeployTimeRequest_fieldAccessorTable;
|
||||||
private static final com.google.protobuf.Descriptors.Descriptor
|
private static final com.google.protobuf.Descriptors.Descriptor
|
||||||
internal_static_galaxy_repository_v1_GetLastDeployTimeReply_descriptor;
|
internal_static_galaxy_repository_v1_GetLastDeployTimeReply_descriptor;
|
||||||
private static final
|
private static final
|
||||||
com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
||||||
internal_static_galaxy_repository_v1_GetLastDeployTimeReply_fieldAccessorTable;
|
internal_static_galaxy_repository_v1_GetLastDeployTimeReply_fieldAccessorTable;
|
||||||
private static final com.google.protobuf.Descriptors.Descriptor
|
private static final com.google.protobuf.Descriptors.Descriptor
|
||||||
internal_static_galaxy_repository_v1_DiscoverHierarchyRequest_descriptor;
|
internal_static_galaxy_repository_v1_DiscoverHierarchyRequest_descriptor;
|
||||||
private static final
|
private static final
|
||||||
com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
||||||
internal_static_galaxy_repository_v1_DiscoverHierarchyRequest_fieldAccessorTable;
|
internal_static_galaxy_repository_v1_DiscoverHierarchyRequest_fieldAccessorTable;
|
||||||
private static final com.google.protobuf.Descriptors.Descriptor
|
private static final com.google.protobuf.Descriptors.Descriptor
|
||||||
internal_static_galaxy_repository_v1_DiscoverHierarchyReply_descriptor;
|
internal_static_galaxy_repository_v1_DiscoverHierarchyReply_descriptor;
|
||||||
private static final
|
private static final
|
||||||
com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
||||||
internal_static_galaxy_repository_v1_DiscoverHierarchyReply_fieldAccessorTable;
|
internal_static_galaxy_repository_v1_DiscoverHierarchyReply_fieldAccessorTable;
|
||||||
private static final com.google.protobuf.Descriptors.Descriptor
|
private static final com.google.protobuf.Descriptors.Descriptor
|
||||||
internal_static_galaxy_repository_v1_WatchDeployEventsRequest_descriptor;
|
internal_static_galaxy_repository_v1_WatchDeployEventsRequest_descriptor;
|
||||||
private static final
|
private static final
|
||||||
com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
||||||
internal_static_galaxy_repository_v1_WatchDeployEventsRequest_fieldAccessorTable;
|
internal_static_galaxy_repository_v1_WatchDeployEventsRequest_fieldAccessorTable;
|
||||||
private static final com.google.protobuf.Descriptors.Descriptor
|
private static final com.google.protobuf.Descriptors.Descriptor
|
||||||
internal_static_galaxy_repository_v1_DeployEvent_descriptor;
|
internal_static_galaxy_repository_v1_DeployEvent_descriptor;
|
||||||
private static final
|
private static final
|
||||||
com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
||||||
internal_static_galaxy_repository_v1_DeployEvent_fieldAccessorTable;
|
internal_static_galaxy_repository_v1_DeployEvent_fieldAccessorTable;
|
||||||
private static final com.google.protobuf.Descriptors.Descriptor
|
private static final com.google.protobuf.Descriptors.Descriptor
|
||||||
internal_static_galaxy_repository_v1_GalaxyObject_descriptor;
|
internal_static_galaxy_repository_v1_GalaxyObject_descriptor;
|
||||||
private static final
|
private static final
|
||||||
com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
||||||
internal_static_galaxy_repository_v1_GalaxyObject_fieldAccessorTable;
|
internal_static_galaxy_repository_v1_GalaxyObject_fieldAccessorTable;
|
||||||
private static final com.google.protobuf.Descriptors.Descriptor
|
private static final com.google.protobuf.Descriptors.Descriptor
|
||||||
internal_static_galaxy_repository_v1_GalaxyAttribute_descriptor;
|
internal_static_galaxy_repository_v1_GalaxyAttribute_descriptor;
|
||||||
private static final
|
private static final
|
||||||
com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
||||||
internal_static_galaxy_repository_v1_GalaxyAttribute_fieldAccessorTable;
|
internal_static_galaxy_repository_v1_GalaxyAttribute_fieldAccessorTable;
|
||||||
|
|
||||||
@@ -10446,8 +10596,8 @@ public final class GalaxyRepositoryOuterClass extends com.google.protobuf.Genera
|
|||||||
"sitory.v1.DiscoverHierarchyReply\022h\n\021Watc" +
|
"sitory.v1.DiscoverHierarchyReply\022h\n\021Watc" +
|
||||||
"hDeployEvents\022..galaxy_repository.v1.Wat" +
|
"hDeployEvents\022..galaxy_repository.v1.Wat" +
|
||||||
"chDeployEventsRequest\032!.galaxy_repositor" +
|
"chDeployEventsRequest\032!.galaxy_repositor" +
|
||||||
"y.v1.DeployEvent0\001B#\252\002 MxGateway.Contrac" +
|
"y.v1.DeployEvent0\001B-\252\002*ZB.MOM.WW.MxGatew" +
|
||||||
"ts.Proto.Galaxyb\006proto3"
|
"ay.Contracts.Proto.Galaxyb\006proto3"
|
||||||
};
|
};
|
||||||
descriptor = com.google.protobuf.Descriptors.FileDescriptor
|
descriptor = com.google.protobuf.Descriptors.FileDescriptor
|
||||||
.internalBuildGeneratedFileFrom(descriptorData,
|
.internalBuildGeneratedFileFrom(descriptorData,
|
||||||
|
|||||||
+34100
-426
File diff suppressed because it is too large
Load Diff
@@ -12608,8 +12608,8 @@ public final class MxaccessWorker extends com.google.protobuf.GeneratedFile {
|
|||||||
"CONVERSION_FAILED\020\010\022\"\n\036WORKER_FAULT_CATE" +
|
"CONVERSION_FAILED\020\010\022\"\n\036WORKER_FAULT_CATE" +
|
||||||
"GORY_STA_HUNG\020\t\022(\n$WORKER_FAULT_CATEGORY" +
|
"GORY_STA_HUNG\020\t\022(\n$WORKER_FAULT_CATEGORY" +
|
||||||
"_QUEUE_OVERFLOW\020\n\022*\n&WORKER_FAULT_CATEGO" +
|
"_QUEUE_OVERFLOW\020\n\022*\n&WORKER_FAULT_CATEGO" +
|
||||||
"RY_SHUTDOWN_TIMEOUT\020\013B\034\252\002\031MxGateway.Cont" +
|
"RY_SHUTDOWN_TIMEOUT\020\013B&\252\002#ZB.MOM.WW.MxGa" +
|
||||||
"racts.Protob\006proto3"
|
"teway.Contracts.Protob\006proto3"
|
||||||
};
|
};
|
||||||
descriptor = com.google.protobuf.Descriptors.FileDescriptor
|
descriptor = com.google.protobuf.Descriptors.FileDescriptor
|
||||||
.internalBuildGeneratedFileFrom(descriptorData,
|
.internalBuildGeneratedFileFrom(descriptorData,
|
||||||
|
|||||||
+2
-2
@@ -3,11 +3,11 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':mxgateway-client')
|
implementation project(':zb-mom-ww-mxgateway-client')
|
||||||
implementation "com.google.protobuf:protobuf-java-util:${protobufVersion}"
|
implementation "com.google.protobuf:protobuf-java-util:${protobufVersion}"
|
||||||
implementation "info.picocli:picocli:${picocliVersion}"
|
implementation "info.picocli:picocli:${picocliVersion}"
|
||||||
}
|
}
|
||||||
|
|
||||||
application {
|
application {
|
||||||
mainClass = 'com.dohertylan.mxgateway.cli.MxGatewayCli'
|
mainClass = 'com.zb.mom.ww.mxgateway.cli.MxGatewayCli'
|
||||||
}
|
}
|
||||||
+711
-10
@@ -1,20 +1,26 @@
|
|||||||
package com.dohertylan.mxgateway.cli;
|
package com.zb.mom.ww.mxgateway.cli;
|
||||||
|
|
||||||
import com.dohertylan.mxgateway.client.DeployEventStream;
|
import com.zb.mom.ww.mxgateway.client.DeployEventStream;
|
||||||
import com.dohertylan.mxgateway.client.GalaxyRepositoryClient;
|
import com.zb.mom.ww.mxgateway.client.GalaxyRepositoryClient;
|
||||||
import com.dohertylan.mxgateway.client.MxEventStream;
|
import com.zb.mom.ww.mxgateway.client.MxEventStream;
|
||||||
import com.dohertylan.mxgateway.client.MxGatewayClient;
|
import com.zb.mom.ww.mxgateway.client.MxGatewayAlarmFeedSubscription;
|
||||||
import com.dohertylan.mxgateway.client.MxGatewayClientOptions;
|
import com.zb.mom.ww.mxgateway.client.MxGatewayClient;
|
||||||
import com.dohertylan.mxgateway.client.MxGatewayClientVersion;
|
import com.zb.mom.ww.mxgateway.client.MxGatewayClientOptions;
|
||||||
import com.dohertylan.mxgateway.client.MxGatewaySecrets;
|
import com.zb.mom.ww.mxgateway.client.MxGatewayClientVersion;
|
||||||
import com.dohertylan.mxgateway.client.MxGatewaySession;
|
import com.zb.mom.ww.mxgateway.client.MxGatewaySecrets;
|
||||||
import com.dohertylan.mxgateway.client.MxValues;
|
import com.zb.mom.ww.mxgateway.client.MxGatewaySession;
|
||||||
|
import com.zb.mom.ww.mxgateway.client.MxValues;
|
||||||
import galaxy_repository.v1.GalaxyRepositoryOuterClass.DeployEvent;
|
import galaxy_repository.v1.GalaxyRepositoryOuterClass.DeployEvent;
|
||||||
import galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyAttribute;
|
import galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyAttribute;
|
||||||
import galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyObject;
|
import galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyObject;
|
||||||
import com.google.protobuf.Message;
|
import com.google.protobuf.Message;
|
||||||
import com.google.protobuf.util.JsonFormat;
|
import com.google.protobuf.util.JsonFormat;
|
||||||
|
import io.grpc.stub.StreamObserver;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
@@ -24,13 +30,27 @@ import java.util.LinkedHashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.ArrayBlockingQueue;
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmReply;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmRequest;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.ActiveAlarmSnapshot;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.AlarmFeedMessage;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.BulkReadResult;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.BulkWriteResult;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.CloseSessionRequest;
|
import mxaccess_gateway.v1.MxaccessGateway.CloseSessionRequest;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.MxCommandReply;
|
import mxaccess_gateway.v1.MxaccessGateway.MxCommandReply;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.MxEvent;
|
import mxaccess_gateway.v1.MxaccessGateway.MxEvent;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.MxValue;
|
import mxaccess_gateway.v1.MxaccessGateway.MxValue;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.OnAlarmTransitionEvent;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.OpenSessionRequest;
|
import mxaccess_gateway.v1.MxaccessGateway.OpenSessionRequest;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.StreamAlarmsRequest;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.SubscribeResult;
|
import mxaccess_gateway.v1.MxaccessGateway.SubscribeResult;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.Write2BulkEntry;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.WriteBulkEntry;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.WriteSecured2BulkEntry;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.WriteSecuredBulkEntry;
|
||||||
import picocli.CommandLine;
|
import picocli.CommandLine;
|
||||||
import picocli.CommandLine.Command;
|
import picocli.CommandLine.Command;
|
||||||
import picocli.CommandLine.Mixin;
|
import picocli.CommandLine.Mixin;
|
||||||
@@ -109,16 +129,108 @@ public final class MxGatewayCli implements Callable<Integer> {
|
|||||||
commandLine.addSubcommand("advise", new AdviseCommand(clientFactory));
|
commandLine.addSubcommand("advise", new AdviseCommand(clientFactory));
|
||||||
commandLine.addSubcommand("subscribe-bulk", new SubscribeBulkCommand(clientFactory));
|
commandLine.addSubcommand("subscribe-bulk", new SubscribeBulkCommand(clientFactory));
|
||||||
commandLine.addSubcommand("unsubscribe-bulk", new UnsubscribeBulkCommand(clientFactory));
|
commandLine.addSubcommand("unsubscribe-bulk", new UnsubscribeBulkCommand(clientFactory));
|
||||||
|
commandLine.addSubcommand("read-bulk", new ReadBulkCommand(clientFactory));
|
||||||
|
commandLine.addSubcommand("write-bulk", new WriteBulkCommand(clientFactory));
|
||||||
|
commandLine.addSubcommand("write2-bulk", new Write2BulkCommand(clientFactory));
|
||||||
|
commandLine.addSubcommand("write-secured-bulk", new WriteSecuredBulkCommand(clientFactory));
|
||||||
|
commandLine.addSubcommand("write-secured2-bulk", new WriteSecured2BulkCommand(clientFactory));
|
||||||
|
commandLine.addSubcommand("bench-read-bulk", new BenchReadBulkCommand(clientFactory));
|
||||||
commandLine.addSubcommand("write", new WriteCommand(clientFactory));
|
commandLine.addSubcommand("write", new WriteCommand(clientFactory));
|
||||||
commandLine.addSubcommand("stream-events", new StreamEventsCommand(clientFactory));
|
commandLine.addSubcommand("stream-events", new StreamEventsCommand(clientFactory));
|
||||||
|
commandLine.addSubcommand("stream-alarms", new StreamAlarmsCommand(clientFactory));
|
||||||
|
commandLine.addSubcommand("acknowledge-alarm", new AcknowledgeAlarmCommand(clientFactory));
|
||||||
commandLine.addSubcommand("smoke", new SmokeCommand(clientFactory));
|
commandLine.addSubcommand("smoke", new SmokeCommand(clientFactory));
|
||||||
commandLine.addSubcommand("galaxy-test", new GalaxyTestConnectionCommand());
|
commandLine.addSubcommand("galaxy-test", new GalaxyTestConnectionCommand());
|
||||||
commandLine.addSubcommand("galaxy-deploy-time", new GalaxyDeployTimeCommand());
|
commandLine.addSubcommand("galaxy-deploy-time", new GalaxyDeployTimeCommand());
|
||||||
commandLine.addSubcommand("galaxy-discover", new GalaxyDiscoverCommand());
|
commandLine.addSubcommand("galaxy-discover", new GalaxyDiscoverCommand());
|
||||||
commandLine.addSubcommand("galaxy-watch", new GalaxyWatchCommand());
|
commandLine.addSubcommand("galaxy-watch", new GalaxyWatchCommand());
|
||||||
|
commandLine.addSubcommand("batch", new BatchCommand(clientFactory));
|
||||||
return commandLine;
|
return commandLine;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Sentinel written to stdout after every command result in batch mode. */
|
||||||
|
static final String BATCH_EOR = "__MXGW_BATCH_EOR__";
|
||||||
|
|
||||||
|
/** Sentinel queued by {@code stream-alarms} to mark a clean end of the alarm feed. */
|
||||||
|
private static final Object ALARM_FEED_END = new Object();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads one CLI invocation per stdin line, executes each via a fresh
|
||||||
|
* {@link CommandLine}, and writes {@value #BATCH_EOR} to stdout after
|
||||||
|
* every result. Errors are written as JSON to stdout so the harness
|
||||||
|
* sees them in the same stream, delimited by the same sentinel. The
|
||||||
|
* loop never terminates on command failure; only stdin EOF (or an
|
||||||
|
* empty line) ends the session.
|
||||||
|
*/
|
||||||
|
@Command(name = "batch", description = "Reads CLI invocations from stdin and executes them sequentially.")
|
||||||
|
static final class BatchCommand implements Callable<Integer> {
|
||||||
|
private final MxGatewayCliClientFactory clientFactory;
|
||||||
|
|
||||||
|
@Spec
|
||||||
|
private CommandSpec spec;
|
||||||
|
|
||||||
|
BatchCommand(MxGatewayCliClientFactory clientFactory) {
|
||||||
|
this.clientFactory = clientFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer call() {
|
||||||
|
PrintWriter out = spec.commandLine().getOut();
|
||||||
|
try (BufferedReader reader = new BufferedReader(
|
||||||
|
new InputStreamReader(System.in, StandardCharsets.UTF_8))) {
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
if (line.isEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
String[] args = line.trim().split("\\s+");
|
||||||
|
if (args.length == 0 || (args.length == 1 && args[0].isEmpty())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
StringWriter cmdOut = new StringWriter();
|
||||||
|
StringWriter cmdErr = new StringWriter();
|
||||||
|
PrintWriter cmdOutWriter = new PrintWriter(cmdOut, true);
|
||||||
|
PrintWriter cmdErrWriter = new PrintWriter(cmdErr, true);
|
||||||
|
try {
|
||||||
|
CommandLine cmd = commandLine(clientFactory);
|
||||||
|
cmd.setOut(cmdOutWriter);
|
||||||
|
cmd.setErr(cmdErrWriter);
|
||||||
|
int exitCode = cmd.execute(args);
|
||||||
|
cmdOutWriter.flush();
|
||||||
|
cmdErrWriter.flush();
|
||||||
|
String cmdOutput = cmdOut.toString();
|
||||||
|
if (!cmdOutput.isEmpty()) {
|
||||||
|
out.print(cmdOutput);
|
||||||
|
}
|
||||||
|
if (exitCode != 0) {
|
||||||
|
// Non-zero exit: emit the stderr content (if any) as a JSON
|
||||||
|
// error object to stdout so the harness can parse it in the
|
||||||
|
// same delimited stream.
|
||||||
|
String errText = cmdErr.toString().trim();
|
||||||
|
if (errText.isEmpty()) {
|
||||||
|
errText = "command exited with code " + exitCode;
|
||||||
|
}
|
||||||
|
Map<String, Object> errorPayload = new LinkedHashMap<>();
|
||||||
|
errorPayload.put("error", errText);
|
||||||
|
errorPayload.put("type", "error");
|
||||||
|
out.println(jsonObject(errorPayload));
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Map<String, Object> errorPayload = new LinkedHashMap<>();
|
||||||
|
errorPayload.put("error", ex.getMessage() != null ? ex.getMessage() : ex.getClass().getName());
|
||||||
|
errorPayload.put("type", "error");
|
||||||
|
out.println(jsonObject(errorPayload));
|
||||||
|
}
|
||||||
|
out.println(BATCH_EOR);
|
||||||
|
out.flush();
|
||||||
|
}
|
||||||
|
} catch (java.io.IOException ex) {
|
||||||
|
// Stdin closed unexpectedly — treat as EOF and exit normally.
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
abstract static class GalaxyCommand implements Callable<Integer> {
|
abstract static class GalaxyCommand implements Callable<Integer> {
|
||||||
@Mixin
|
@Mixin
|
||||||
CommonOptions common = new CommonOptions();
|
CommonOptions common = new CommonOptions();
|
||||||
@@ -518,6 +630,359 @@ public final class MxGatewayCli implements Callable<Integer> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Command(name = "read-bulk", description = "Invokes MXAccess ReadBulk (cached or snapshot per tag).")
|
||||||
|
static final class ReadBulkCommand extends GatewayCommand {
|
||||||
|
@Option(names = "--session-id", required = true, description = "Gateway session id.")
|
||||||
|
String sessionId;
|
||||||
|
|
||||||
|
@Option(names = "--server-handle", required = true, description = "MXAccess server handle.")
|
||||||
|
int serverHandle;
|
||||||
|
|
||||||
|
@Option(names = "--items", required = true, description = "Comma-separated tag addresses.")
|
||||||
|
String items;
|
||||||
|
|
||||||
|
@Option(
|
||||||
|
names = "--timeout-ms",
|
||||||
|
defaultValue = "0",
|
||||||
|
description = "Per-tag snapshot timeout in milliseconds (0 = worker default).")
|
||||||
|
int timeoutMs;
|
||||||
|
|
||||||
|
ReadBulkCommand(MxGatewayCliClientFactory clientFactory) {
|
||||||
|
super(clientFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer call() {
|
||||||
|
try (MxGatewayCliClient client = clientFactory.connect(common.resolved())) {
|
||||||
|
List<BulkReadResult> results = client.session(sessionId)
|
||||||
|
.readBulk(serverHandle, parseStringList(items), Duration.ofMillis(timeoutMs));
|
||||||
|
writeReadBulkOutput("read-bulk", common, json, results);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Command(name = "write-bulk", description = "Invokes MXAccess WriteBulk.")
|
||||||
|
static final class WriteBulkCommand extends GatewayCommand {
|
||||||
|
@Option(names = "--session-id", required = true, description = "Gateway session id.")
|
||||||
|
String sessionId;
|
||||||
|
|
||||||
|
@Option(names = "--server-handle", required = true, description = "MXAccess server handle.")
|
||||||
|
int serverHandle;
|
||||||
|
|
||||||
|
@Option(names = "--item-handles", required = true, description = "Comma-separated item handles.")
|
||||||
|
String itemHandles;
|
||||||
|
|
||||||
|
@Option(names = "--type", defaultValue = "string", description = "Value type for all entries.")
|
||||||
|
String type;
|
||||||
|
|
||||||
|
@Option(names = "--values", required = true, description = "Comma-separated values, one per item handle.")
|
||||||
|
String values;
|
||||||
|
|
||||||
|
@Option(names = "--user-id", defaultValue = "0", description = "MXAccess user id.")
|
||||||
|
int userId;
|
||||||
|
|
||||||
|
WriteBulkCommand(MxGatewayCliClientFactory clientFactory) {
|
||||||
|
super(clientFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer call() {
|
||||||
|
try (MxGatewayCliClient client = clientFactory.connect(common.resolved())) {
|
||||||
|
List<Integer> handles = parseIntList(itemHandles);
|
||||||
|
List<String> valueTexts = parseStringList(values);
|
||||||
|
if (handles.size() != valueTexts.size()) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"item-handles count (" + handles.size() + ") does not match values count ("
|
||||||
|
+ valueTexts.size() + ")");
|
||||||
|
}
|
||||||
|
List<WriteBulkEntry> entries = new ArrayList<>(handles.size());
|
||||||
|
for (int i = 0; i < handles.size(); i++) {
|
||||||
|
entries.add(WriteBulkEntry.newBuilder()
|
||||||
|
.setItemHandle(handles.get(i))
|
||||||
|
.setUserId(userId)
|
||||||
|
.setValue(parseValue(type, valueTexts.get(i)))
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
List<BulkWriteResult> results = client.session(sessionId).writeBulk(serverHandle, entries);
|
||||||
|
writeWriteBulkOutput("write-bulk", common, json, results);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Command(name = "write2-bulk", description = "Invokes MXAccess Write2Bulk (timestamped).")
|
||||||
|
static final class Write2BulkCommand extends GatewayCommand {
|
||||||
|
@Option(names = "--session-id", required = true, description = "Gateway session id.")
|
||||||
|
String sessionId;
|
||||||
|
|
||||||
|
@Option(names = "--server-handle", required = true, description = "MXAccess server handle.")
|
||||||
|
int serverHandle;
|
||||||
|
|
||||||
|
@Option(names = "--item-handles", required = true, description = "Comma-separated item handles.")
|
||||||
|
String itemHandles;
|
||||||
|
|
||||||
|
@Option(names = "--type", defaultValue = "string", description = "Value type for all entries.")
|
||||||
|
String type;
|
||||||
|
|
||||||
|
@Option(names = "--values", required = true, description = "Comma-separated values, one per item handle.")
|
||||||
|
String values;
|
||||||
|
|
||||||
|
@Option(names = "--timestamp", required = true, description = "ISO-8601 timestamp shared across all entries.")
|
||||||
|
String timestamp;
|
||||||
|
|
||||||
|
@Option(names = "--user-id", defaultValue = "0", description = "MXAccess user id.")
|
||||||
|
int userId;
|
||||||
|
|
||||||
|
Write2BulkCommand(MxGatewayCliClientFactory clientFactory) {
|
||||||
|
super(clientFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer call() {
|
||||||
|
try (MxGatewayCliClient client = clientFactory.connect(common.resolved())) {
|
||||||
|
List<Integer> handles = parseIntList(itemHandles);
|
||||||
|
List<String> valueTexts = parseStringList(values);
|
||||||
|
if (handles.size() != valueTexts.size()) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"item-handles count (" + handles.size() + ") does not match values count ("
|
||||||
|
+ valueTexts.size() + ")");
|
||||||
|
}
|
||||||
|
MxValue timestampValue = MxValues.timestampValue(Instant.parse(timestamp));
|
||||||
|
List<Write2BulkEntry> entries = new ArrayList<>(handles.size());
|
||||||
|
for (int i = 0; i < handles.size(); i++) {
|
||||||
|
entries.add(Write2BulkEntry.newBuilder()
|
||||||
|
.setItemHandle(handles.get(i))
|
||||||
|
.setUserId(userId)
|
||||||
|
.setValue(parseValue(type, valueTexts.get(i)))
|
||||||
|
.setTimestampValue(timestampValue)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
List<BulkWriteResult> results = client.session(sessionId).write2Bulk(serverHandle, entries);
|
||||||
|
writeWriteBulkOutput("write2-bulk", common, json, results);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Command(name = "write-secured-bulk", description = "Invokes MXAccess WriteSecuredBulk.")
|
||||||
|
static final class WriteSecuredBulkCommand extends GatewayCommand {
|
||||||
|
@Option(names = "--session-id", required = true, description = "Gateway session id.")
|
||||||
|
String sessionId;
|
||||||
|
|
||||||
|
@Option(names = "--server-handle", required = true, description = "MXAccess server handle.")
|
||||||
|
int serverHandle;
|
||||||
|
|
||||||
|
@Option(names = "--item-handles", required = true, description = "Comma-separated item handles.")
|
||||||
|
String itemHandles;
|
||||||
|
|
||||||
|
@Option(names = "--type", defaultValue = "string", description = "Value type for all entries.")
|
||||||
|
String type;
|
||||||
|
|
||||||
|
@Option(names = "--values", required = true, description = "Comma-separated values, one per item handle.")
|
||||||
|
String values;
|
||||||
|
|
||||||
|
@Option(names = "--current-user-id", defaultValue = "0", description = "MXAccess current user id.")
|
||||||
|
int currentUserId;
|
||||||
|
|
||||||
|
@Option(names = "--verifier-user-id", defaultValue = "0", description = "MXAccess verifier user id.")
|
||||||
|
int verifierUserId;
|
||||||
|
|
||||||
|
WriteSecuredBulkCommand(MxGatewayCliClientFactory clientFactory) {
|
||||||
|
super(clientFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer call() {
|
||||||
|
try (MxGatewayCliClient client = clientFactory.connect(common.resolved())) {
|
||||||
|
List<Integer> handles = parseIntList(itemHandles);
|
||||||
|
List<String> valueTexts = parseStringList(values);
|
||||||
|
if (handles.size() != valueTexts.size()) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"item-handles count (" + handles.size() + ") does not match values count ("
|
||||||
|
+ valueTexts.size() + ")");
|
||||||
|
}
|
||||||
|
List<WriteSecuredBulkEntry> entries = new ArrayList<>(handles.size());
|
||||||
|
for (int i = 0; i < handles.size(); i++) {
|
||||||
|
entries.add(WriteSecuredBulkEntry.newBuilder()
|
||||||
|
.setItemHandle(handles.get(i))
|
||||||
|
.setCurrentUserId(currentUserId)
|
||||||
|
.setVerifierUserId(verifierUserId)
|
||||||
|
.setValue(parseValue(type, valueTexts.get(i)))
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
List<BulkWriteResult> results = client.session(sessionId).writeSecuredBulk(serverHandle, entries);
|
||||||
|
writeWriteBulkOutput("write-secured-bulk", common, json, results);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Command(name = "write-secured2-bulk", description = "Invokes MXAccess WriteSecured2Bulk.")
|
||||||
|
static final class WriteSecured2BulkCommand extends GatewayCommand {
|
||||||
|
@Option(names = "--session-id", required = true, description = "Gateway session id.")
|
||||||
|
String sessionId;
|
||||||
|
|
||||||
|
@Option(names = "--server-handle", required = true, description = "MXAccess server handle.")
|
||||||
|
int serverHandle;
|
||||||
|
|
||||||
|
@Option(names = "--item-handles", required = true, description = "Comma-separated item handles.")
|
||||||
|
String itemHandles;
|
||||||
|
|
||||||
|
@Option(names = "--type", defaultValue = "string", description = "Value type for all entries.")
|
||||||
|
String type;
|
||||||
|
|
||||||
|
@Option(names = "--values", required = true, description = "Comma-separated values, one per item handle.")
|
||||||
|
String values;
|
||||||
|
|
||||||
|
@Option(names = "--timestamp", required = true, description = "ISO-8601 timestamp shared across all entries.")
|
||||||
|
String timestamp;
|
||||||
|
|
||||||
|
@Option(names = "--current-user-id", defaultValue = "0", description = "MXAccess current user id.")
|
||||||
|
int currentUserId;
|
||||||
|
|
||||||
|
@Option(names = "--verifier-user-id", defaultValue = "0", description = "MXAccess verifier user id.")
|
||||||
|
int verifierUserId;
|
||||||
|
|
||||||
|
WriteSecured2BulkCommand(MxGatewayCliClientFactory clientFactory) {
|
||||||
|
super(clientFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer call() {
|
||||||
|
try (MxGatewayCliClient client = clientFactory.connect(common.resolved())) {
|
||||||
|
List<Integer> handles = parseIntList(itemHandles);
|
||||||
|
List<String> valueTexts = parseStringList(values);
|
||||||
|
if (handles.size() != valueTexts.size()) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"item-handles count (" + handles.size() + ") does not match values count ("
|
||||||
|
+ valueTexts.size() + ")");
|
||||||
|
}
|
||||||
|
MxValue timestampValue = MxValues.timestampValue(Instant.parse(timestamp));
|
||||||
|
List<WriteSecured2BulkEntry> entries = new ArrayList<>(handles.size());
|
||||||
|
for (int i = 0; i < handles.size(); i++) {
|
||||||
|
entries.add(WriteSecured2BulkEntry.newBuilder()
|
||||||
|
.setItemHandle(handles.get(i))
|
||||||
|
.setCurrentUserId(currentUserId)
|
||||||
|
.setVerifierUserId(verifierUserId)
|
||||||
|
.setValue(parseValue(type, valueTexts.get(i)))
|
||||||
|
.setTimestampValue(timestampValue)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
List<BulkWriteResult> results = client.session(sessionId).writeSecured2Bulk(serverHandle, entries);
|
||||||
|
writeWriteBulkOutput("write-secured2-bulk", common, json, results);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Command(
|
||||||
|
name = "bench-read-bulk",
|
||||||
|
description = "Repeatedly invokes ReadBulk for benchmarking; prints aggregate timing.")
|
||||||
|
static final class BenchReadBulkCommand extends GatewayCommand {
|
||||||
|
@Option(names = "--session-id", required = true, description = "Gateway session id.")
|
||||||
|
String sessionId;
|
||||||
|
|
||||||
|
@Option(names = "--server-handle", required = true, description = "MXAccess server handle.")
|
||||||
|
int serverHandle;
|
||||||
|
|
||||||
|
@Option(names = "--items", required = true, description = "Comma-separated tag addresses.")
|
||||||
|
String items;
|
||||||
|
|
||||||
|
@Option(
|
||||||
|
names = "--timeout-ms",
|
||||||
|
defaultValue = "0",
|
||||||
|
description = "Per-tag snapshot timeout in milliseconds (0 = worker default).")
|
||||||
|
int timeoutMs;
|
||||||
|
|
||||||
|
@Option(names = "--iterations", defaultValue = "10", description = "Number of ReadBulk calls to perform.")
|
||||||
|
int iterations;
|
||||||
|
|
||||||
|
@Option(
|
||||||
|
names = "--warmup",
|
||||||
|
defaultValue = "1",
|
||||||
|
description = "Number of warmup iterations excluded from timing.")
|
||||||
|
int warmup;
|
||||||
|
|
||||||
|
BenchReadBulkCommand(MxGatewayCliClientFactory clientFactory) {
|
||||||
|
super(clientFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer call() {
|
||||||
|
if (iterations <= 0) {
|
||||||
|
throw new IllegalArgumentException("--iterations must be positive");
|
||||||
|
}
|
||||||
|
if (warmup < 0) {
|
||||||
|
throw new IllegalArgumentException("--warmup must be non-negative");
|
||||||
|
}
|
||||||
|
List<String> tagAddresses = parseStringList(items);
|
||||||
|
Duration timeout = Duration.ofMillis(timeoutMs);
|
||||||
|
try (MxGatewayCliClient client = clientFactory.connect(common.resolved())) {
|
||||||
|
MxGatewayCliSession session = client.session(sessionId);
|
||||||
|
for (int i = 0; i < warmup; i++) {
|
||||||
|
session.readBulk(serverHandle, tagAddresses, timeout);
|
||||||
|
}
|
||||||
|
long totalNanos = 0L;
|
||||||
|
long minNanos = Long.MAX_VALUE;
|
||||||
|
long maxNanos = 0L;
|
||||||
|
int lastResultCount = 0;
|
||||||
|
int lastSuccessCount = 0;
|
||||||
|
int lastCachedCount = 0;
|
||||||
|
for (int i = 0; i < iterations; i++) {
|
||||||
|
long start = System.nanoTime();
|
||||||
|
List<BulkReadResult> results = session.readBulk(serverHandle, tagAddresses, timeout);
|
||||||
|
long elapsed = System.nanoTime() - start;
|
||||||
|
totalNanos += elapsed;
|
||||||
|
minNanos = Math.min(minNanos, elapsed);
|
||||||
|
maxNanos = Math.max(maxNanos, elapsed);
|
||||||
|
lastResultCount = results.size();
|
||||||
|
lastSuccessCount = 0;
|
||||||
|
lastCachedCount = 0;
|
||||||
|
for (BulkReadResult result : results) {
|
||||||
|
if (result.getWasSuccessful()) {
|
||||||
|
lastSuccessCount++;
|
||||||
|
}
|
||||||
|
if (result.getWasCached()) {
|
||||||
|
lastCachedCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
double avgMs = totalNanos / 1_000_000.0 / iterations;
|
||||||
|
double minMs = minNanos / 1_000_000.0;
|
||||||
|
double maxMs = maxNanos / 1_000_000.0;
|
||||||
|
PrintWriter out = common.spec.commandLine().getOut();
|
||||||
|
if (json) {
|
||||||
|
Map<String, Object> output = new LinkedHashMap<>();
|
||||||
|
output.put("command", "bench-read-bulk");
|
||||||
|
output.put("options", common.redactedJsonMap());
|
||||||
|
output.put("iterations", iterations);
|
||||||
|
output.put("warmup", warmup);
|
||||||
|
output.put("tagCount", tagAddresses.size());
|
||||||
|
output.put("resultCount", lastResultCount);
|
||||||
|
output.put("successCount", lastSuccessCount);
|
||||||
|
output.put("cachedCount", lastCachedCount);
|
||||||
|
output.put("avgMs", avgMs);
|
||||||
|
output.put("minMs", minMs);
|
||||||
|
output.put("maxMs", maxMs);
|
||||||
|
out.println(jsonObject(output));
|
||||||
|
} else {
|
||||||
|
out.printf(
|
||||||
|
"iterations=%d tags=%d avg=%.3fms min=%.3fms max=%.3fms last_results=%d last_success=%d last_cached=%d%n",
|
||||||
|
iterations,
|
||||||
|
tagAddresses.size(),
|
||||||
|
avgMs,
|
||||||
|
minMs,
|
||||||
|
maxMs,
|
||||||
|
lastResultCount,
|
||||||
|
lastSuccessCount,
|
||||||
|
lastCachedCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Command(name = "write", description = "Invokes MXAccess Write.")
|
@Command(name = "write", description = "Invokes MXAccess Write.")
|
||||||
static final class WriteCommand extends GatewayCommand {
|
static final class WriteCommand extends GatewayCommand {
|
||||||
@Option(names = "--session-id", required = true, description = "Gateway session id.")
|
@Option(names = "--session-id", required = true, description = "Gateway session id.")
|
||||||
@@ -591,6 +1056,115 @@ public final class MxGatewayCli implements Callable<Integer> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Command(name = "stream-alarms", description = "Streams the gateway central alarm feed.")
|
||||||
|
static final class StreamAlarmsCommand extends GatewayCommand {
|
||||||
|
@Option(names = "--filter-prefix", description = "Alarm-reference prefix scoping the feed; empty means unscoped.")
|
||||||
|
String filterPrefix = "";
|
||||||
|
|
||||||
|
@Option(names = "--limit", defaultValue = "0", description = "Maximum feed messages to print.")
|
||||||
|
int limit;
|
||||||
|
|
||||||
|
StreamAlarmsCommand(MxGatewayCliClientFactory clientFactory) {
|
||||||
|
super(clientFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer call() {
|
||||||
|
try (MxGatewayCliClient client = clientFactory.connect(common.resolved())) {
|
||||||
|
// The async alarm feed delivers on a background gRPC thread; buffer
|
||||||
|
// messages in a bounded queue and drain them on this thread so the
|
||||||
|
// --limit termination mirrors stream-events. 1024 absorbs the
|
||||||
|
// gateway's initial active-alarm snapshot burst.
|
||||||
|
BlockingQueue<Object> queue = new ArrayBlockingQueue<>(1024);
|
||||||
|
StreamAlarmsRequest request = StreamAlarmsRequest.newBuilder()
|
||||||
|
.setAlarmFilterPrefix(filterPrefix)
|
||||||
|
.build();
|
||||||
|
MxGatewayAlarmFeedSubscription subscription =
|
||||||
|
client.streamAlarms(request, new StreamObserver<>() {
|
||||||
|
@Override
|
||||||
|
public void onNext(AlarmFeedMessage value) {
|
||||||
|
queue.offer(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Throwable error) {
|
||||||
|
queue.offer(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCompleted() {
|
||||||
|
queue.offer(ALARM_FEED_END);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
int count = 0;
|
||||||
|
while (true) {
|
||||||
|
Object item = queue.take();
|
||||||
|
if (item == ALARM_FEED_END) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (item instanceof Throwable error) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"gateway stream alarms failed: " + error.getMessage(), error);
|
||||||
|
}
|
||||||
|
AlarmFeedMessage message = (AlarmFeedMessage) item;
|
||||||
|
if (json) {
|
||||||
|
client.out().println(protoJson(message));
|
||||||
|
} else {
|
||||||
|
client.out().println(formatAlarmFeedMessage(message));
|
||||||
|
}
|
||||||
|
client.out().flush();
|
||||||
|
count++;
|
||||||
|
if (limit > 0 && count >= limit) {
|
||||||
|
subscription.cancel();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (InterruptedException error) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
subscription.cancel();
|
||||||
|
} finally {
|
||||||
|
subscription.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Command(name = "acknowledge-alarm", description = "Acknowledges an active MXAccess alarm.")
|
||||||
|
static final class AcknowledgeAlarmCommand extends GatewayCommand {
|
||||||
|
@Option(names = "--reference", required = true, description = "Full alarm reference to acknowledge.")
|
||||||
|
String reference;
|
||||||
|
|
||||||
|
@Option(names = "--comment", description = "Operator acknowledge comment.")
|
||||||
|
String comment = "";
|
||||||
|
|
||||||
|
@Option(names = "--operator", description = "Operator user performing the acknowledge.")
|
||||||
|
String operator = "";
|
||||||
|
|
||||||
|
AcknowledgeAlarmCommand(MxGatewayCliClientFactory clientFactory) {
|
||||||
|
super(clientFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer call() {
|
||||||
|
try (MxGatewayCliClient client = clientFactory.connect(common.resolved())) {
|
||||||
|
AcknowledgeAlarmReply reply = client.acknowledgeAlarm(AcknowledgeAlarmRequest.newBuilder()
|
||||||
|
.setAlarmFullReference(reference)
|
||||||
|
.setComment(comment)
|
||||||
|
.setOperatorUser(operator)
|
||||||
|
.build());
|
||||||
|
writeOutput(
|
||||||
|
"acknowledge-alarm",
|
||||||
|
common,
|
||||||
|
json,
|
||||||
|
reply,
|
||||||
|
() -> Integer.toString(reply.getHresult()));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Command(name = "smoke", description = "Runs a bounded open/register/add/advise flow.")
|
@Command(name = "smoke", description = "Runs a bounded open/register/add/advise flow.")
|
||||||
static final class SmokeCommand extends GatewayCommand {
|
static final class SmokeCommand extends GatewayCommand {
|
||||||
@Option(names = "--client-name", defaultValue = "mxgw-java-smoke", description = "MXAccess client name.")
|
@Option(names = "--client-name", defaultValue = "mxgw-java-smoke", description = "MXAccess client name.")
|
||||||
@@ -710,6 +1284,11 @@ public final class MxGatewayCli implements Callable<Integer> {
|
|||||||
|
|
||||||
MxGatewayCliSession session(String sessionId);
|
MxGatewayCliSession session(String sessionId);
|
||||||
|
|
||||||
|
AcknowledgeAlarmReply acknowledgeAlarm(AcknowledgeAlarmRequest request);
|
||||||
|
|
||||||
|
MxGatewayAlarmFeedSubscription streamAlarms(
|
||||||
|
StreamAlarmsRequest request, StreamObserver<AlarmFeedMessage> observer);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void close();
|
void close();
|
||||||
}
|
}
|
||||||
@@ -733,6 +1312,16 @@ public final class MxGatewayCli implements Callable<Integer> {
|
|||||||
|
|
||||||
List<SubscribeResult> unsubscribeBulk(int serverHandle, List<Integer> itemHandles);
|
List<SubscribeResult> unsubscribeBulk(int serverHandle, List<Integer> itemHandles);
|
||||||
|
|
||||||
|
List<BulkReadResult> readBulk(int serverHandle, List<String> items, Duration timeout);
|
||||||
|
|
||||||
|
List<BulkWriteResult> writeBulk(int serverHandle, List<WriteBulkEntry> entries);
|
||||||
|
|
||||||
|
List<BulkWriteResult> write2Bulk(int serverHandle, List<Write2BulkEntry> entries);
|
||||||
|
|
||||||
|
List<BulkWriteResult> writeSecuredBulk(int serverHandle, List<WriteSecuredBulkEntry> entries);
|
||||||
|
|
||||||
|
List<BulkWriteResult> writeSecured2Bulk(int serverHandle, List<WriteSecured2BulkEntry> entries);
|
||||||
|
|
||||||
MxEventStream streamEventsAfter(long afterWorkerSequence);
|
MxEventStream streamEventsAfter(long afterWorkerSequence);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -772,6 +1361,17 @@ public final class MxGatewayCli implements Callable<Integer> {
|
|||||||
return new GrpcMxGatewayCliSession(MxGatewaySession.forSessionId(client, sessionId));
|
return new GrpcMxGatewayCliSession(MxGatewaySession.forSessionId(client, sessionId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AcknowledgeAlarmReply acknowledgeAlarm(AcknowledgeAlarmRequest request) {
|
||||||
|
return client.acknowledgeAlarm(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MxGatewayAlarmFeedSubscription streamAlarms(
|
||||||
|
StreamAlarmsRequest request, StreamObserver<AlarmFeedMessage> observer) {
|
||||||
|
return client.streamAlarms(request, observer);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
client.close();
|
client.close();
|
||||||
@@ -824,6 +1424,31 @@ public final class MxGatewayCli implements Callable<Integer> {
|
|||||||
return session.unsubscribeBulk(serverHandle, itemHandles);
|
return session.unsubscribeBulk(serverHandle, itemHandles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<BulkReadResult> readBulk(int serverHandle, List<String> items, Duration timeout) {
|
||||||
|
return session.readBulk(serverHandle, items, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<BulkWriteResult> writeBulk(int serverHandle, List<WriteBulkEntry> entries) {
|
||||||
|
return session.writeBulk(serverHandle, entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<BulkWriteResult> write2Bulk(int serverHandle, List<Write2BulkEntry> entries) {
|
||||||
|
return session.write2Bulk(serverHandle, entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<BulkWriteResult> writeSecuredBulk(int serverHandle, List<WriteSecuredBulkEntry> entries) {
|
||||||
|
return session.writeSecuredBulk(serverHandle, entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<BulkWriteResult> writeSecured2Bulk(int serverHandle, List<WriteSecured2BulkEntry> entries) {
|
||||||
|
return session.writeSecured2Bulk(serverHandle, entries);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MxEventStream streamEventsAfter(long afterWorkerSequence) {
|
public MxEventStream streamEventsAfter(long afterWorkerSequence) {
|
||||||
return session.streamEventsAfter(afterWorkerSequence);
|
return session.streamEventsAfter(afterWorkerSequence);
|
||||||
@@ -872,6 +1497,82 @@ public final class MxGatewayCli implements Callable<Integer> {
|
|||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void writeWriteBulkOutput(
|
||||||
|
String command, CommonOptions common, boolean json, List<BulkWriteResult> results) {
|
||||||
|
PrintWriter out = common.spec.commandLine().getOut();
|
||||||
|
if (json) {
|
||||||
|
Map<String, Object> output = new LinkedHashMap<>();
|
||||||
|
output.put("command", command);
|
||||||
|
output.put("options", common.redactedJsonMap());
|
||||||
|
output.put("results", results.stream().map(MxGatewayCli::bulkWriteResultMap).toList());
|
||||||
|
out.println(jsonObject(output));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
out.println(results.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, Object> bulkWriteResultMap(BulkWriteResult result) {
|
||||||
|
Map<String, Object> values = new LinkedHashMap<>();
|
||||||
|
values.put("serverHandle", result.getServerHandle());
|
||||||
|
values.put("itemHandle", result.getItemHandle());
|
||||||
|
values.put("wasSuccessful", result.getWasSuccessful());
|
||||||
|
values.put("hresult", result.hasHresult() ? (Object) result.getHresult() : null);
|
||||||
|
values.put("errorMessage", result.getErrorMessage());
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void writeReadBulkOutput(
|
||||||
|
String command, CommonOptions common, boolean json, List<BulkReadResult> results) {
|
||||||
|
PrintWriter out = common.spec.commandLine().getOut();
|
||||||
|
if (json) {
|
||||||
|
Map<String, Object> output = new LinkedHashMap<>();
|
||||||
|
output.put("command", command);
|
||||||
|
output.put("options", common.redactedJsonMap());
|
||||||
|
output.put("results", results.stream().map(MxGatewayCli::bulkReadResultMap).toList());
|
||||||
|
out.println(jsonObject(output));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
out.println(results.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, Object> bulkReadResultMap(BulkReadResult result) {
|
||||||
|
Map<String, Object> values = new LinkedHashMap<>();
|
||||||
|
values.put("serverHandle", result.getServerHandle());
|
||||||
|
values.put("tagAddress", result.getTagAddress());
|
||||||
|
values.put("itemHandle", result.getItemHandle());
|
||||||
|
values.put("wasSuccessful", result.getWasSuccessful());
|
||||||
|
values.put("wasCached", result.getWasCached());
|
||||||
|
values.put("quality", result.getQuality());
|
||||||
|
values.put("errorMessage", result.getErrorMessage());
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders one {@link AlarmFeedMessage} in the CLI's plain-text output
|
||||||
|
* style, distinguishing the active-alarm snapshot, snapshot-complete
|
||||||
|
* sentinel, and transition cases of the message's {@code payload} oneof.
|
||||||
|
*/
|
||||||
|
private static String formatAlarmFeedMessage(AlarmFeedMessage message) {
|
||||||
|
return switch (message.getPayloadCase()) {
|
||||||
|
case ACTIVE_ALARM -> {
|
||||||
|
ActiveAlarmSnapshot alarm = message.getActiveAlarm();
|
||||||
|
yield String.format(
|
||||||
|
"active-alarm %s state=%s severity=%d",
|
||||||
|
alarm.getAlarmFullReference(), alarm.getCurrentState().name(), alarm.getSeverity());
|
||||||
|
}
|
||||||
|
case SNAPSHOT_COMPLETE -> "snapshot-complete";
|
||||||
|
case TRANSITION -> {
|
||||||
|
OnAlarmTransitionEvent transition = message.getTransition();
|
||||||
|
yield String.format(
|
||||||
|
"transition %s kind=%s severity=%d",
|
||||||
|
transition.getAlarmFullReference(),
|
||||||
|
transition.getTransitionKind().name(),
|
||||||
|
transition.getSeverity());
|
||||||
|
}
|
||||||
|
case PAYLOAD_NOT_SET -> "unknown";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private static MxValue parseValue(String type, String text) {
|
private static MxValue parseValue(String type, String text) {
|
||||||
return switch (type) {
|
return switch (type) {
|
||||||
case "bool" -> MxValues.boolValue(Boolean.parseBoolean(text));
|
case "bool" -> MxValues.boolValue(Boolean.parseBoolean(text));
|
||||||
+230
-2
@@ -1,27 +1,47 @@
|
|||||||
package com.dohertylan.mxgateway.cli;
|
package com.zb.mom.ww.mxgateway.cli;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import com.zb.mom.ww.mxgateway.client.MxGatewayAlarmFeedSubscription;
|
||||||
|
import io.grpc.stub.StreamObserver;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmReply;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmRequest;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.ActiveAlarmSnapshot;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.AddItemReply;
|
import mxaccess_gateway.v1.MxaccessGateway.AddItemReply;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.AlarmConditionState;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.AlarmFeedMessage;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.AlarmTransitionKind;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.BulkReadResult;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.BulkWriteResult;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.CloseSessionReply;
|
import mxaccess_gateway.v1.MxaccessGateway.CloseSessionReply;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.CloseSessionRequest;
|
import mxaccess_gateway.v1.MxaccessGateway.CloseSessionRequest;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.MxCommandKind;
|
import mxaccess_gateway.v1.MxaccessGateway.MxCommandKind;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.MxCommandReply;
|
import mxaccess_gateway.v1.MxaccessGateway.MxCommandReply;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.MxEvent;
|
import mxaccess_gateway.v1.MxaccessGateway.MxEvent;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.MxValue;
|
import mxaccess_gateway.v1.MxaccessGateway.MxValue;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.OnAlarmTransitionEvent;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.OpenSessionReply;
|
import mxaccess_gateway.v1.MxaccessGateway.OpenSessionReply;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.OpenSessionRequest;
|
import mxaccess_gateway.v1.MxaccessGateway.OpenSessionRequest;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.ProtocolStatus;
|
import mxaccess_gateway.v1.MxaccessGateway.ProtocolStatus;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.ProtocolStatusCode;
|
import mxaccess_gateway.v1.MxaccessGateway.ProtocolStatusCode;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.RegisterReply;
|
import mxaccess_gateway.v1.MxaccessGateway.RegisterReply;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.SessionState;
|
import mxaccess_gateway.v1.MxaccessGateway.SessionState;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.StreamAlarmsRequest;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.SubscribeResult;
|
import mxaccess_gateway.v1.MxaccessGateway.SubscribeResult;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.Write2BulkEntry;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.WriteBulkEntry;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.WriteSecured2BulkEntry;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.WriteSecuredBulkEntry;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
final class MxGatewayCliTests {
|
final class MxGatewayCliTests {
|
||||||
@@ -141,6 +161,111 @@ final class MxGatewayCliTests {
|
|||||||
assertTrue(run.output().contains("\"wasSuccessful\":true"));
|
assertTrue(run.output().contains("\"wasSuccessful\":true"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- stream-alarms / acknowledge-alarm subcommands ----
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void streamAlarmsCommandForwardsFilterPrefixAndPrintsFeedMessages() {
|
||||||
|
FakeClientFactory factory = new FakeClientFactory();
|
||||||
|
CliRun run = execute(factory, "stream-alarms", "--filter-prefix", "Tank01");
|
||||||
|
|
||||||
|
assertEquals(0, run.exitCode());
|
||||||
|
assertEquals("Tank01", factory.client.lastStreamAlarmsRequest.getAlarmFilterPrefix());
|
||||||
|
String out = run.output();
|
||||||
|
assertTrue(out.contains("active-alarm Tank01.Level.HiHi"), out);
|
||||||
|
assertTrue(out.contains("snapshot-complete"), out);
|
||||||
|
assertTrue(out.contains("transition Tank01.Level.HiHi"), out);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void streamAlarmsCommandHonoursLimit() {
|
||||||
|
FakeClientFactory factory = new FakeClientFactory();
|
||||||
|
CliRun run = execute(factory, "stream-alarms", "--limit", "1");
|
||||||
|
|
||||||
|
assertEquals(0, run.exitCode());
|
||||||
|
long lines = run.output().lines().filter(line -> !line.isBlank()).count();
|
||||||
|
assertEquals(1, lines, "expected exactly one feed message with --limit 1, got: " + run.output());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void streamAlarmsCommandPrintsJson() {
|
||||||
|
FakeClientFactory factory = new FakeClientFactory();
|
||||||
|
CliRun run = execute(factory, "stream-alarms", "--json");
|
||||||
|
|
||||||
|
assertEquals(0, run.exitCode());
|
||||||
|
assertTrue(run.output().contains("\"activeAlarm\""), run.output());
|
||||||
|
assertTrue(run.output().contains("\"snapshotComplete\""), run.output());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void acknowledgeAlarmCommandForwardsOptionsAndPrintsReply() {
|
||||||
|
FakeClientFactory factory = new FakeClientFactory();
|
||||||
|
CliRun run = execute(
|
||||||
|
factory,
|
||||||
|
"acknowledge-alarm",
|
||||||
|
"--reference",
|
||||||
|
"Tank01.Level.HiHi",
|
||||||
|
"--comment",
|
||||||
|
"checked",
|
||||||
|
"--operator",
|
||||||
|
"operator1",
|
||||||
|
"--json");
|
||||||
|
|
||||||
|
assertEquals(0, run.exitCode());
|
||||||
|
assertEquals("Tank01.Level.HiHi", factory.client.lastAcknowledgeAlarmRequest.getAlarmFullReference());
|
||||||
|
assertEquals("checked", factory.client.lastAcknowledgeAlarmRequest.getComment());
|
||||||
|
assertEquals("operator1", factory.client.lastAcknowledgeAlarmRequest.getOperatorUser());
|
||||||
|
assertTrue(run.output().contains("\"command\":\"acknowledge-alarm\""), run.output());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void acknowledgeAlarmCommandRequiresReference() {
|
||||||
|
CliRun run = execute(new FakeClientFactory(), "acknowledge-alarm", "--comment", "checked");
|
||||||
|
|
||||||
|
assertFalse(run.exitCode() == 0, "expected non-zero exit without --reference");
|
||||||
|
assertTrue(run.errors().contains("--reference"), run.errors());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void batchCommandExecutesVersionAndEmitsEorMarker() {
|
||||||
|
CliRun run = executeBatch(new FakeClientFactory(), "version --json\n");
|
||||||
|
|
||||||
|
assertEquals(0, run.exitCode());
|
||||||
|
String out = run.output();
|
||||||
|
assertTrue(out.contains("\"clientVersion\""), out);
|
||||||
|
assertTrue(out.contains(MxGatewayCli.BATCH_EOR), out);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void batchCommandEmitsEorAfterFailedCommandAndContinues() {
|
||||||
|
// An unknown subcommand causes a picocli parse error (non-zero exit).
|
||||||
|
// The loop must still emit BATCH_EOR for the failure and continue
|
||||||
|
// processing the subsequent valid command.
|
||||||
|
CliRun run = executeBatch(new FakeClientFactory(), "no-such-subcommand\nversion --json\n");
|
||||||
|
|
||||||
|
assertEquals(0, run.exitCode());
|
||||||
|
String out = run.output();
|
||||||
|
long eorCount = out.lines()
|
||||||
|
.filter(l -> l.equals(MxGatewayCli.BATCH_EOR))
|
||||||
|
.count();
|
||||||
|
assertEquals(2, eorCount, "expected exactly 2 EOR sentinels, got: " + eorCount + "\nOutput:\n" + out);
|
||||||
|
assertTrue(out.contains("\"clientVersion\""), out);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the CLI with {@code batch} as the subcommand, using the provided
|
||||||
|
* string as standard input content. Temporarily replaces {@link System#in}
|
||||||
|
* for the duration of the call.
|
||||||
|
*/
|
||||||
|
private static CliRun executeBatch(MxGatewayCli.MxGatewayCliClientFactory factory, String stdinContent) {
|
||||||
|
InputStream originalIn = System.in;
|
||||||
|
try {
|
||||||
|
System.setIn(new ByteArrayInputStream(stdinContent.getBytes(StandardCharsets.UTF_8)));
|
||||||
|
return execute(factory, "batch");
|
||||||
|
} finally {
|
||||||
|
System.setIn(originalIn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static CliRun execute(MxGatewayCli.MxGatewayCliClientFactory factory, String... args) {
|
private static CliRun execute(MxGatewayCli.MxGatewayCliClientFactory factory, String... args) {
|
||||||
StringWriter output = new StringWriter();
|
StringWriter output = new StringWriter();
|
||||||
StringWriter errors = new StringWriter();
|
StringWriter errors = new StringWriter();
|
||||||
@@ -169,6 +294,8 @@ final class MxGatewayCliTests {
|
|||||||
private final PrintWriter out;
|
private final PrintWriter out;
|
||||||
private final FakeSession session = new FakeSession();
|
private final FakeSession session = new FakeSession();
|
||||||
private boolean closeCalled;
|
private boolean closeCalled;
|
||||||
|
private AcknowledgeAlarmRequest lastAcknowledgeAlarmRequest;
|
||||||
|
private StreamAlarmsRequest lastStreamAlarmsRequest;
|
||||||
|
|
||||||
private FakeClient(PrintWriter out) {
|
private FakeClient(PrintWriter out) {
|
||||||
this.out = out;
|
this.out = out;
|
||||||
@@ -202,6 +329,40 @@ final class MxGatewayCliTests {
|
|||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AcknowledgeAlarmReply acknowledgeAlarm(AcknowledgeAlarmRequest request) {
|
||||||
|
lastAcknowledgeAlarmRequest = request;
|
||||||
|
return AcknowledgeAlarmReply.newBuilder()
|
||||||
|
.setCorrelationId(request.getClientCorrelationId())
|
||||||
|
.setProtocolStatus(ok())
|
||||||
|
.setHresult(0)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MxGatewayAlarmFeedSubscription streamAlarms(
|
||||||
|
StreamAlarmsRequest request, StreamObserver<AlarmFeedMessage> observer) {
|
||||||
|
lastStreamAlarmsRequest = request;
|
||||||
|
// Replay a deterministic active-alarm snapshot, snapshot-complete
|
||||||
|
// sentinel, transition, then complete the feed so the CLI command
|
||||||
|
// drains a bounded stream without contacting a live gateway.
|
||||||
|
observer.onNext(AlarmFeedMessage.newBuilder()
|
||||||
|
.setActiveAlarm(ActiveAlarmSnapshot.newBuilder()
|
||||||
|
.setAlarmFullReference("Tank01.Level.HiHi")
|
||||||
|
.setCurrentState(AlarmConditionState.ALARM_CONDITION_STATE_ACTIVE)
|
||||||
|
.setSeverity(700))
|
||||||
|
.build());
|
||||||
|
observer.onNext(AlarmFeedMessage.newBuilder().setSnapshotComplete(true).build());
|
||||||
|
observer.onNext(AlarmFeedMessage.newBuilder()
|
||||||
|
.setTransition(OnAlarmTransitionEvent.newBuilder()
|
||||||
|
.setAlarmFullReference("Tank01.Level.HiHi")
|
||||||
|
.setTransitionKind(AlarmTransitionKind.ALARM_TRANSITION_KIND_ACKNOWLEDGE)
|
||||||
|
.setSeverity(700))
|
||||||
|
.build());
|
||||||
|
observer.onCompleted();
|
||||||
|
return new MxGatewayAlarmFeedSubscription();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
}
|
}
|
||||||
@@ -296,7 +457,74 @@ final class MxGatewayCliTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public com.dohertylan.mxgateway.client.MxEventStream streamEventsAfter(long afterWorkerSequence) {
|
public List<BulkReadResult> readBulk(int serverHandle, List<String> items, Duration timeout) {
|
||||||
|
List<BulkReadResult> results = new ArrayList<>();
|
||||||
|
for (int index = 0; index < items.size(); index++) {
|
||||||
|
results.add(BulkReadResult.newBuilder()
|
||||||
|
.setServerHandle(serverHandle)
|
||||||
|
.setTagAddress(items.get(index))
|
||||||
|
.setItemHandle(200 + index)
|
||||||
|
.setWasSuccessful(true)
|
||||||
|
.setWasCached(true)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<BulkWriteResult> writeBulk(int serverHandle, List<WriteBulkEntry> entries) {
|
||||||
|
List<BulkWriteResult> results = new ArrayList<>();
|
||||||
|
for (WriteBulkEntry entry : entries) {
|
||||||
|
results.add(BulkWriteResult.newBuilder()
|
||||||
|
.setServerHandle(serverHandle)
|
||||||
|
.setItemHandle(entry.getItemHandle())
|
||||||
|
.setWasSuccessful(true)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<BulkWriteResult> write2Bulk(int serverHandle, List<Write2BulkEntry> entries) {
|
||||||
|
List<BulkWriteResult> results = new ArrayList<>();
|
||||||
|
for (Write2BulkEntry entry : entries) {
|
||||||
|
results.add(BulkWriteResult.newBuilder()
|
||||||
|
.setServerHandle(serverHandle)
|
||||||
|
.setItemHandle(entry.getItemHandle())
|
||||||
|
.setWasSuccessful(true)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<BulkWriteResult> writeSecuredBulk(int serverHandle, List<WriteSecuredBulkEntry> entries) {
|
||||||
|
List<BulkWriteResult> results = new ArrayList<>();
|
||||||
|
for (WriteSecuredBulkEntry entry : entries) {
|
||||||
|
results.add(BulkWriteResult.newBuilder()
|
||||||
|
.setServerHandle(serverHandle)
|
||||||
|
.setItemHandle(entry.getItemHandle())
|
||||||
|
.setWasSuccessful(true)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<BulkWriteResult> writeSecured2Bulk(int serverHandle, List<WriteSecured2BulkEntry> entries) {
|
||||||
|
List<BulkWriteResult> results = new ArrayList<>();
|
||||||
|
for (WriteSecured2BulkEntry entry : entries) {
|
||||||
|
results.add(BulkWriteResult.newBuilder()
|
||||||
|
.setServerHandle(serverHandle)
|
||||||
|
.setItemHandle(entry.getItemHandle())
|
||||||
|
.setWasSuccessful(true)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public com.zb.mom.ww.mxgateway.client.MxEventStream streamEventsAfter(long afterWorkerSequence) {
|
||||||
throw new UnsupportedOperationException("stream-events is covered by client tests");
|
throw new UnsupportedOperationException("stream-events is covered by client tests");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+1
-1
@@ -22,7 +22,7 @@ dependencies {
|
|||||||
sourceSets {
|
sourceSets {
|
||||||
main {
|
main {
|
||||||
proto {
|
proto {
|
||||||
srcDir rootProject.file('../../src/MxGateway.Contracts/Protos')
|
srcDir rootProject.file('../../src/ZB.MOM.WW.MxGateway.Contracts/Protos')
|
||||||
include 'mxaccess_gateway.proto'
|
include 'mxaccess_gateway.proto'
|
||||||
include 'mxaccess_worker.proto'
|
include 'mxaccess_worker.proto'
|
||||||
include 'galaxy_repository.proto'
|
include 'galaxy_repository.proto'
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.dohertylan.mxgateway.client;
|
package com.zb.mom.ww.mxgateway.client;
|
||||||
|
|
||||||
import galaxy_repository.v1.GalaxyRepositoryOuterClass.DeployEvent;
|
import galaxy_repository.v1.GalaxyRepositoryOuterClass.DeployEvent;
|
||||||
import galaxy_repository.v1.GalaxyRepositoryOuterClass.WatchDeployEventsRequest;
|
import galaxy_repository.v1.GalaxyRepositoryOuterClass.WatchDeployEventsRequest;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.dohertylan.mxgateway.client;
|
package com.zb.mom.ww.mxgateway.client;
|
||||||
|
|
||||||
import galaxy_repository.v1.GalaxyRepositoryOuterClass.DeployEvent;
|
import galaxy_repository.v1.GalaxyRepositoryOuterClass.DeployEvent;
|
||||||
import galaxy_repository.v1.GalaxyRepositoryOuterClass.WatchDeployEventsRequest;
|
import galaxy_repository.v1.GalaxyRepositoryOuterClass.WatchDeployEventsRequest;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.dohertylan.mxgateway.client;
|
package com.zb.mom.ww.mxgateway.client;
|
||||||
|
|
||||||
import com.google.common.util.concurrent.FutureCallback;
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
import com.google.common.util.concurrent.Futures;
|
import com.google.common.util.concurrent.Futures;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.dohertylan.mxgateway.client;
|
package com.zb.mom.ww.mxgateway.client;
|
||||||
|
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.MxCommandReply;
|
import mxaccess_gateway.v1.MxaccessGateway.MxCommandReply;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.ProtocolStatus;
|
import mxaccess_gateway.v1.MxaccessGateway.ProtocolStatus;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.dohertylan.mxgateway.client;
|
package com.zb.mom.ww.mxgateway.client;
|
||||||
|
|
||||||
import io.grpc.Status;
|
import io.grpc.Status;
|
||||||
import io.grpc.StatusRuntimeException;
|
import io.grpc.StatusRuntimeException;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.dohertylan.mxgateway.client;
|
package com.zb.mom.ww.mxgateway.client;
|
||||||
|
|
||||||
import io.grpc.stub.ClientCallStreamObserver;
|
import io.grpc.stub.ClientCallStreamObserver;
|
||||||
import io.grpc.stub.ClientResponseObserver;
|
import io.grpc.stub.ClientResponseObserver;
|
||||||
+67
@@ -0,0 +1,67 @@
|
|||||||
|
package com.zb.mom.ww.mxgateway.client;
|
||||||
|
|
||||||
|
import io.grpc.stub.ClientCallStreamObserver;
|
||||||
|
import io.grpc.stub.ClientResponseObserver;
|
||||||
|
import io.grpc.stub.StreamObserver;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.AlarmFeedMessage;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.StreamAlarmsRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancellable handle returned by {@code streamAlarms}.
|
||||||
|
*
|
||||||
|
* <p>Wraps a caller-supplied {@link StreamObserver} and exposes a
|
||||||
|
* {@link #cancel()} entry point that aborts the underlying gRPC call. The
|
||||||
|
* subscription also implements {@link AutoCloseable} so it can participate in
|
||||||
|
* try-with-resources blocks.
|
||||||
|
*/
|
||||||
|
public final class MxGatewayAlarmFeedSubscription implements AutoCloseable {
|
||||||
|
private final AtomicReference<ClientCallStreamObserver<StreamAlarmsRequest>> requestStream = new AtomicReference<>();
|
||||||
|
private final AtomicBoolean cancelled = new AtomicBoolean();
|
||||||
|
|
||||||
|
ClientResponseObserver<StreamAlarmsRequest, AlarmFeedMessage> wrap(StreamObserver<AlarmFeedMessage> observer) {
|
||||||
|
return new ClientResponseObserver<>() {
|
||||||
|
@Override
|
||||||
|
public void beforeStart(ClientCallStreamObserver<StreamAlarmsRequest> stream) {
|
||||||
|
requestStream.set(stream);
|
||||||
|
if (cancelled.get()) {
|
||||||
|
stream.cancel("client cancelled alarm feed", null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNext(AlarmFeedMessage value) {
|
||||||
|
observer.onNext(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Throwable error) {
|
||||||
|
observer.onError(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCompleted() {
|
||||||
|
observer.onCompleted();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancels the underlying gRPC call. Safe to invoke before the call has
|
||||||
|
* started; cancellation is recorded and applied as soon as the stream
|
||||||
|
* attaches.
|
||||||
|
*/
|
||||||
|
public void cancel() {
|
||||||
|
cancelled.set(true);
|
||||||
|
ClientCallStreamObserver<StreamAlarmsRequest> stream = requestStream.get();
|
||||||
|
if (stream != null) {
|
||||||
|
stream.cancel("client cancelled alarm feed", null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.dohertylan.mxgateway.client;
|
package com.zb.mom.ww.mxgateway.client;
|
||||||
|
|
||||||
import io.grpc.CallOptions;
|
import io.grpc.CallOptions;
|
||||||
import io.grpc.Channel;
|
import io.grpc.Channel;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.dohertylan.mxgateway.client;
|
package com.zb.mom.ww.mxgateway.client;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thrown when the gateway rejects a call because the supplied API key is
|
* Thrown when the gateway rejects a call because the supplied API key is
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.dohertylan.mxgateway.client;
|
package com.zb.mom.ww.mxgateway.client;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thrown when the gateway accepts an API key but rejects a call because the
|
* Thrown when the gateway accepts an API key but rejects a call because the
|
||||||
+24
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.dohertylan.mxgateway.client;
|
package com.zb.mom.ww.mxgateway.client;
|
||||||
|
|
||||||
import com.google.common.util.concurrent.FutureCallback;
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
import com.google.common.util.concurrent.Futures;
|
import com.google.common.util.concurrent.Futures;
|
||||||
@@ -18,6 +18,7 @@ import mxaccess_gateway.v1.MxAccessGatewayGrpc;
|
|||||||
import mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmReply;
|
import mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmReply;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmRequest;
|
import mxaccess_gateway.v1.MxaccessGateway.AcknowledgeAlarmRequest;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.ActiveAlarmSnapshot;
|
import mxaccess_gateway.v1.MxaccessGateway.ActiveAlarmSnapshot;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.AlarmFeedMessage;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.CloseSessionReply;
|
import mxaccess_gateway.v1.MxaccessGateway.CloseSessionReply;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.CloseSessionRequest;
|
import mxaccess_gateway.v1.MxaccessGateway.CloseSessionRequest;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.MxCommandReply;
|
import mxaccess_gateway.v1.MxaccessGateway.MxCommandReply;
|
||||||
@@ -27,6 +28,7 @@ import mxaccess_gateway.v1.MxaccessGateway.OpenSessionReply;
|
|||||||
import mxaccess_gateway.v1.MxaccessGateway.OpenSessionRequest;
|
import mxaccess_gateway.v1.MxaccessGateway.OpenSessionRequest;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.ProtocolStatusCode;
|
import mxaccess_gateway.v1.MxaccessGateway.ProtocolStatusCode;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.QueryActiveAlarmsRequest;
|
import mxaccess_gateway.v1.MxaccessGateway.QueryActiveAlarmsRequest;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.StreamAlarmsRequest;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.StreamEventsRequest;
|
import mxaccess_gateway.v1.MxaccessGateway.StreamEventsRequest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -320,6 +322,27 @@ public final class MxGatewayClient implements AutoCloseable {
|
|||||||
return subscription;
|
return subscription;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches to the gateway's central alarm feed. The stream opens with one
|
||||||
|
* {@code AlarmFeedMessage} per currently-active alarm (the ConditionRefresh
|
||||||
|
* snapshot), then a single {@code snapshot_complete}, then a
|
||||||
|
* {@code transition} for every subsequent raise / acknowledge / clear.
|
||||||
|
*
|
||||||
|
* <p>Served by the gateway's always-on alarm monitor — no worker session is
|
||||||
|
* opened — so any number of clients may attach.
|
||||||
|
*
|
||||||
|
* @param request the {@code StreamAlarmsRequest}, optionally scoped by
|
||||||
|
* alarm-reference prefix
|
||||||
|
* @param observer caller-supplied observer that receives feed messages and completion
|
||||||
|
* @return a cancellable subscription handle
|
||||||
|
*/
|
||||||
|
public MxGatewayAlarmFeedSubscription streamAlarms(
|
||||||
|
StreamAlarmsRequest request, StreamObserver<AlarmFeedMessage> observer) {
|
||||||
|
MxGatewayAlarmFeedSubscription subscription = new MxGatewayAlarmFeedSubscription();
|
||||||
|
withStreamDeadline(rawAsyncStub()).streamAlarms(request, subscription.wrap(observer));
|
||||||
|
return subscription;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
if (ownedChannel != null) {
|
if (ownedChannel != null) {
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.dohertylan.mxgateway.client;
|
package com.zb.mom.ww.mxgateway.client;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.dohertylan.mxgateway.client;
|
package com.zb.mom.ww.mxgateway.client;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reports the client and protocol version numbers compiled into this build.
|
* Reports the client and protocol version numbers compiled into this build.
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.dohertylan.mxgateway.client;
|
package com.zb.mom.ww.mxgateway.client;
|
||||||
|
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.MxCommandReply;
|
import mxaccess_gateway.v1.MxaccessGateway.MxCommandReply;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.ProtocolStatus;
|
import mxaccess_gateway.v1.MxaccessGateway.ProtocolStatus;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.dohertylan.mxgateway.client;
|
package com.zb.mom.ww.mxgateway.client;
|
||||||
|
|
||||||
import io.grpc.Status;
|
import io.grpc.Status;
|
||||||
import io.grpc.StatusRuntimeException;
|
import io.grpc.StatusRuntimeException;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.dohertylan.mxgateway.client;
|
package com.zb.mom.ww.mxgateway.client;
|
||||||
|
|
||||||
import io.grpc.stub.ClientCallStreamObserver;
|
import io.grpc.stub.ClientCallStreamObserver;
|
||||||
import io.grpc.stub.ClientResponseObserver;
|
import io.grpc.stub.ClientResponseObserver;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.dohertylan.mxgateway.client;
|
package com.zb.mom.ww.mxgateway.client;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base unchecked exception thrown by the MXAccess Gateway Java client.
|
* Base unchecked exception thrown by the MXAccess Gateway Java client.
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.dohertylan.mxgateway.client;
|
package com.zb.mom.ww.mxgateway.client;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helpers for redacting secrets such as gateway API keys from log output.
|
* Helpers for redacting secrets such as gateway API keys from log output.
|
||||||
+149
-1
@@ -1,6 +1,7 @@
|
|||||||
package com.dohertylan.mxgateway.client;
|
package com.zb.mom.ww.mxgateway.client;
|
||||||
|
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.HexFormat;
|
import java.util.HexFormat;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@@ -9,6 +10,8 @@ import mxaccess_gateway.v1.MxaccessGateway.AddItemBulkCommand;
|
|||||||
import mxaccess_gateway.v1.MxaccessGateway.AddItemCommand;
|
import mxaccess_gateway.v1.MxaccessGateway.AddItemCommand;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.AdviseItemBulkCommand;
|
import mxaccess_gateway.v1.MxaccessGateway.AdviseItemBulkCommand;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.AdviseCommand;
|
import mxaccess_gateway.v1.MxaccessGateway.AdviseCommand;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.BulkReadResult;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.BulkWriteResult;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.CloseSessionReply;
|
import mxaccess_gateway.v1.MxaccessGateway.CloseSessionReply;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.CloseSessionRequest;
|
import mxaccess_gateway.v1.MxaccessGateway.CloseSessionRequest;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.MxCommand;
|
import mxaccess_gateway.v1.MxaccessGateway.MxCommand;
|
||||||
@@ -17,6 +20,7 @@ import mxaccess_gateway.v1.MxaccessGateway.MxCommandReply;
|
|||||||
import mxaccess_gateway.v1.MxaccessGateway.MxCommandRequest;
|
import mxaccess_gateway.v1.MxaccessGateway.MxCommandRequest;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.MxValue;
|
import mxaccess_gateway.v1.MxaccessGateway.MxValue;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.OpenSessionReply;
|
import mxaccess_gateway.v1.MxaccessGateway.OpenSessionReply;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.ReadBulkCommand;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.RegisterCommand;
|
import mxaccess_gateway.v1.MxaccessGateway.RegisterCommand;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.RemoveItemBulkCommand;
|
import mxaccess_gateway.v1.MxaccessGateway.RemoveItemBulkCommand;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.RemoveItemCommand;
|
import mxaccess_gateway.v1.MxaccessGateway.RemoveItemCommand;
|
||||||
@@ -27,8 +31,16 @@ import mxaccess_gateway.v1.MxaccessGateway.UnAdviseCommand;
|
|||||||
import mxaccess_gateway.v1.MxaccessGateway.UnAdviseItemBulkCommand;
|
import mxaccess_gateway.v1.MxaccessGateway.UnAdviseItemBulkCommand;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.UnsubscribeBulkCommand;
|
import mxaccess_gateway.v1.MxaccessGateway.UnsubscribeBulkCommand;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.UnregisterCommand;
|
import mxaccess_gateway.v1.MxaccessGateway.UnregisterCommand;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.Write2BulkCommand;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.Write2BulkEntry;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.Write2Command;
|
import mxaccess_gateway.v1.MxaccessGateway.Write2Command;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.WriteBulkCommand;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.WriteBulkEntry;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.WriteCommand;
|
import mxaccess_gateway.v1.MxaccessGateway.WriteCommand;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.WriteSecured2BulkCommand;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.WriteSecured2BulkEntry;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.WriteSecuredBulkCommand;
|
||||||
|
import mxaccess_gateway.v1.MxaccessGateway.WriteSecuredBulkEntry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Typed handle for a single MXAccess gateway session.
|
* Typed handle for a single MXAccess gateway session.
|
||||||
@@ -421,6 +433,142 @@ public final class MxGatewaySession implements AutoCloseable {
|
|||||||
return reply.getUnsubscribeBulk().getResultsList();
|
return reply.getUnsubscribeBulk().getResultsList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bulk {@code Write} — sequential MXAccess Write per entry on the worker's STA.
|
||||||
|
*
|
||||||
|
* <p>Per-entry failures appear as {@link BulkWriteResult} entries with
|
||||||
|
* {@code wasSuccessful == false}; this method does not throw for per-entry
|
||||||
|
* MXAccess failures (it still throws {@link MxGatewayException} on transport
|
||||||
|
* or protocol-level failures).
|
||||||
|
*
|
||||||
|
* @param serverHandle the {@code ServerHandle} owning the items
|
||||||
|
* @param entries the per-item (handle, value, user id) tuples
|
||||||
|
* @return a per-entry {@link BulkWriteResult} list
|
||||||
|
* @throws MxGatewayException on transport or protocol failure
|
||||||
|
* @throws NullPointerException if {@code entries} is {@code null}
|
||||||
|
*/
|
||||||
|
public List<BulkWriteResult> writeBulk(int serverHandle, List<WriteBulkEntry> entries) {
|
||||||
|
Objects.requireNonNull(entries, "entries");
|
||||||
|
MxCommandReply reply = invokeCommand(MxCommand.newBuilder()
|
||||||
|
.setKind(MxCommandKind.MX_COMMAND_KIND_WRITE_BULK)
|
||||||
|
.setWriteBulk(WriteBulkCommand.newBuilder()
|
||||||
|
.setServerHandle(serverHandle)
|
||||||
|
.addAllEntries(entries))
|
||||||
|
.build());
|
||||||
|
return reply.getWriteBulk().getResultsList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bulk {@code Write2} — sequential MXAccess Write2 (timestamped) per entry.
|
||||||
|
*
|
||||||
|
* <p>Per-entry semantics mirror {@link #writeBulk(int, List)}.
|
||||||
|
*
|
||||||
|
* @param serverHandle the {@code ServerHandle} owning the items
|
||||||
|
* @param entries the per-item (handle, value, timestamp, user id) tuples
|
||||||
|
* @return a per-entry {@link BulkWriteResult} list
|
||||||
|
* @throws MxGatewayException on transport or protocol failure
|
||||||
|
* @throws NullPointerException if {@code entries} is {@code null}
|
||||||
|
*/
|
||||||
|
public List<BulkWriteResult> write2Bulk(int serverHandle, List<Write2BulkEntry> entries) {
|
||||||
|
Objects.requireNonNull(entries, "entries");
|
||||||
|
MxCommandReply reply = invokeCommand(MxCommand.newBuilder()
|
||||||
|
.setKind(MxCommandKind.MX_COMMAND_KIND_WRITE2_BULK)
|
||||||
|
.setWrite2Bulk(Write2BulkCommand.newBuilder()
|
||||||
|
.setServerHandle(serverHandle)
|
||||||
|
.addAllEntries(entries))
|
||||||
|
.build());
|
||||||
|
return reply.getWrite2Bulk().getResultsList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bulk {@code WriteSecured} — credential-sensitive values must not be logged
|
||||||
|
* by callers; mirrors the single-item write-secured redaction contract.
|
||||||
|
*
|
||||||
|
* <p>Per-entry semantics mirror {@link #writeBulk(int, List)}.
|
||||||
|
*
|
||||||
|
* @param serverHandle the {@code ServerHandle} owning the items
|
||||||
|
* @param entries the per-item (handle, value, current+verifier user id) tuples
|
||||||
|
* @return a per-entry {@link BulkWriteResult} list
|
||||||
|
* @throws MxGatewayException on transport or protocol failure
|
||||||
|
* @throws NullPointerException if {@code entries} is {@code null}
|
||||||
|
*/
|
||||||
|
public List<BulkWriteResult> writeSecuredBulk(int serverHandle, List<WriteSecuredBulkEntry> entries) {
|
||||||
|
Objects.requireNonNull(entries, "entries");
|
||||||
|
MxCommandReply reply = invokeCommand(MxCommand.newBuilder()
|
||||||
|
.setKind(MxCommandKind.MX_COMMAND_KIND_WRITE_SECURED_BULK)
|
||||||
|
.setWriteSecuredBulk(WriteSecuredBulkCommand.newBuilder()
|
||||||
|
.setServerHandle(serverHandle)
|
||||||
|
.addAllEntries(entries))
|
||||||
|
.build());
|
||||||
|
return reply.getWriteSecuredBulk().getResultsList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bulk {@code WriteSecured2} — sequential timestamped + verified write per entry.
|
||||||
|
*
|
||||||
|
* <p>Per-entry semantics mirror {@link #writeBulk(int, List)}.
|
||||||
|
*
|
||||||
|
* @param serverHandle the {@code ServerHandle} owning the items
|
||||||
|
* @param entries the per-item (handle, value, timestamp, current+verifier user id) tuples
|
||||||
|
* @return a per-entry {@link BulkWriteResult} list
|
||||||
|
* @throws MxGatewayException on transport or protocol failure
|
||||||
|
* @throws NullPointerException if {@code entries} is {@code null}
|
||||||
|
*/
|
||||||
|
public List<BulkWriteResult> writeSecured2Bulk(int serverHandle, List<WriteSecured2BulkEntry> entries) {
|
||||||
|
Objects.requireNonNull(entries, "entries");
|
||||||
|
MxCommandReply reply = invokeCommand(MxCommand.newBuilder()
|
||||||
|
.setKind(MxCommandKind.MX_COMMAND_KIND_WRITE_SECURED2_BULK)
|
||||||
|
.setWriteSecured2Bulk(WriteSecured2BulkCommand.newBuilder()
|
||||||
|
.setServerHandle(serverHandle)
|
||||||
|
.addAllEntries(entries))
|
||||||
|
.build());
|
||||||
|
return reply.getWriteSecured2Bulk().getResultsList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bulk {@code Read} — snapshot the current value of each requested tag.
|
||||||
|
*
|
||||||
|
* <p>MXAccess COM has no synchronous read; the worker returns the cached
|
||||||
|
* {@code OnDataChange} value for any tag that is already advised
|
||||||
|
* ({@code wasCached == true}) without modifying the existing subscription,
|
||||||
|
* and falls back to a full AddItem + Advise + wait + UnAdvise + RemoveItem
|
||||||
|
* snapshot lifecycle otherwise. The supplied {@code timeout} bounds the
|
||||||
|
* per-tag wait in the snapshot case; pass {@link Duration#ZERO} (or
|
||||||
|
* {@code null}) to use the worker default (1000 ms). Per-tag failures
|
||||||
|
* appear as {@link BulkReadResult} entries with {@code wasSuccessful == false};
|
||||||
|
* this method does not throw for per-tag MXAccess failures.
|
||||||
|
*
|
||||||
|
* @param serverHandle the {@code ServerHandle} owning the items
|
||||||
|
* @param tagAddresses the tag addresses to read
|
||||||
|
* @param timeout per-tag snapshot timeout (zero or null = worker default)
|
||||||
|
* @return a per-tag {@link BulkReadResult} list
|
||||||
|
* @throws MxGatewayException on transport or protocol failure
|
||||||
|
* @throws NullPointerException if {@code tagAddresses} is {@code null}
|
||||||
|
* @throws IllegalArgumentException if {@code timeout} is negative or exceeds {@link Integer#MAX_VALUE} milliseconds
|
||||||
|
*/
|
||||||
|
public List<BulkReadResult> readBulk(int serverHandle, List<String> tagAddresses, Duration timeout) {
|
||||||
|
Objects.requireNonNull(tagAddresses, "tagAddresses");
|
||||||
|
int timeoutMs = 0;
|
||||||
|
if (timeout != null) {
|
||||||
|
if (timeout.isNegative()) {
|
||||||
|
throw new IllegalArgumentException("timeout must be non-negative");
|
||||||
|
}
|
||||||
|
long millis = timeout.toMillis();
|
||||||
|
if (millis > Integer.MAX_VALUE) {
|
||||||
|
throw new IllegalArgumentException("timeout exceeds Integer.MAX_VALUE milliseconds");
|
||||||
|
}
|
||||||
|
timeoutMs = (int) millis;
|
||||||
|
}
|
||||||
|
MxCommandReply reply = invokeCommand(MxCommand.newBuilder()
|
||||||
|
.setKind(MxCommandKind.MX_COMMAND_KIND_READ_BULK)
|
||||||
|
.setReadBulk(ReadBulkCommand.newBuilder()
|
||||||
|
.setServerHandle(serverHandle)
|
||||||
|
.addAllTagAddresses(tagAddresses)
|
||||||
|
.setTimeoutMs(timeoutMs))
|
||||||
|
.build());
|
||||||
|
return reply.getReadBulk().getResultsList();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invokes MXAccess {@code Write}.
|
* Invokes MXAccess {@code Write}.
|
||||||
*
|
*
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.dohertylan.mxgateway.client;
|
package com.zb.mom.ww.mxgateway.client;
|
||||||
|
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.ProtocolStatus;
|
import mxaccess_gateway.v1.MxaccessGateway.ProtocolStatus;
|
||||||
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.dohertylan.mxgateway.client;
|
package com.zb.mom.ww.mxgateway.client;
|
||||||
|
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.ProtocolStatus;
|
import mxaccess_gateway.v1.MxaccessGateway.ProtocolStatus;
|
||||||
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.dohertylan.mxgateway.client;
|
package com.zb.mom.ww.mxgateway.client;
|
||||||
|
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.MxStatusCategory;
|
import mxaccess_gateway.v1.MxaccessGateway.MxStatusCategory;
|
||||||
import mxaccess_gateway.v1.MxaccessGateway.MxStatusProxy;
|
import mxaccess_gateway.v1.MxaccessGateway.MxStatusProxy;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.dohertylan.mxgateway.client;
|
package com.zb.mom.ww.mxgateway.client;
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
import com.google.protobuf.Timestamp;
|
import com.google.protobuf.Timestamp;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.dohertylan.mxgateway.client;
|
package com.zb.mom.ww.mxgateway.client;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.dohertylan.mxgateway.client;
|
package com.zb.mom.ww.mxgateway.client;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user