From f1a7c34b69f99c65ef24ca185e741f2eb388e582 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Fri, 11 Sep 2026 10:08:28 -0400 Subject: [PATCH] feat(cli): --mes-reportable on template alarm add/update and instance alarm-override set Claude-Session: https://claude.ai/code/session_01Y5ef34iUUMjmwnMpL9xgmB --- .../Commands/InstanceCommands.cs | 6 ++++-- .../Commands/TemplateCommands.cs | 12 ++++++++++-- src/ZB.MOM.WW.ScadaBridge.CLI/README.md | 11 +++++++---- .../EditingParityTests.cs | 4 ++++ 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/ZB.MOM.WW.ScadaBridge.CLI/Commands/InstanceCommands.cs b/src/ZB.MOM.WW.ScadaBridge.CLI/Commands/InstanceCommands.cs index 9c00f3f5..37e372c2 100644 --- a/src/ZB.MOM.WW.ScadaBridge.CLI/Commands/InstanceCommands.cs +++ b/src/ZB.MOM.WW.ScadaBridge.CLI/Commands/InstanceCommands.cs @@ -553,8 +553,9 @@ public static class InstanceCommands var setAlarmOption = new Option("--alarm") { Description = "Alarm canonical name (e.g., 'TempLevels' or 'Pump.TempSensor.Heat')", Required = true }; var setConfigOption = new Option("--trigger-config") { Description = "JSON override for TriggerConfiguration (HiLo: partial merge; others: whole-replace)" }; var setPriorityOption = new Option("--priority") { Description = "Priority override (0-1000)" }; + var setMesReportableOption = new Option("--mes-reportable") { Description = "MES-reportable override: true or false; omit to inherit the template value" }; var setCmd = new Command("set") { Description = "Set (upsert) an alarm override on an instance" }; - setCmd.Add(setIdOption); setCmd.Add(setAlarmOption); setCmd.Add(setConfigOption); setCmd.Add(setPriorityOption); + setCmd.Add(setIdOption); setCmd.Add(setAlarmOption); setCmd.Add(setConfigOption); setCmd.Add(setPriorityOption); setCmd.Add(setMesReportableOption); setCmd.SetAction(async (ParseResult result) => { return await CommandHelpers.ExecuteCommandAsync( @@ -563,7 +564,8 @@ public static class InstanceCommands result.GetValue(setIdOption), result.GetValue(setAlarmOption)!, result.GetValue(setConfigOption), - result.GetValue(setPriorityOption))); + result.GetValue(setPriorityOption), + result.GetValue(setMesReportableOption))); }); group.Add(setCmd); diff --git a/src/ZB.MOM.WW.ScadaBridge.CLI/Commands/TemplateCommands.cs b/src/ZB.MOM.WW.ScadaBridge.CLI/Commands/TemplateCommands.cs index e13e46e3..a64066c1 100644 --- a/src/ZB.MOM.WW.ScadaBridge.CLI/Commands/TemplateCommands.cs +++ b/src/ZB.MOM.WW.ScadaBridge.CLI/Commands/TemplateCommands.cs @@ -682,6 +682,8 @@ public static class TemplateCommands var triggerConfigOption = new Option("--trigger-config") { Description = "Trigger configuration JSON" }; var lockedOption = new Option("--locked") { Description = "Lock status" }; lockedOption.DefaultValueFactory = _ => false; + var mesReportableOption = new Option("--mes-reportable") { Description = "Report this alarm to MES (IsFlaggedForMES on the alarm-status API)" }; + mesReportableOption.DefaultValueFactory = _ => false; // Typed setpoint flags (alternative to raw --trigger-config; raw wins when both supplied). var attributeOption = new Option("--attribute") { Description = "Attribute name the trigger watches (all trigger types except Expression)" }; @@ -714,6 +716,7 @@ public static class TemplateCommands addCmd.Add(descOption); addCmd.Add(triggerConfigOption); addCmd.Add(lockedOption); + addCmd.Add(mesReportableOption); addCmd.Add(attributeOption); addCmd.Add(matchValueOption); addCmd.Add(notEqualsOption); @@ -751,7 +754,8 @@ public static class TemplateCommands result.GetValue(priorityOption)!, result.GetValue(descOption), triggerConfig, - result.GetValue(lockedOption))); + result.GetValue(lockedOption), + result.GetValue(mesReportableOption))); }); group.Add(addCmd); @@ -763,6 +767,8 @@ public static class TemplateCommands var updateTriggerConfigOption = new Option("--trigger-config") { Description = "Trigger configuration JSON" }; var updateLockedOption = new Option("--locked") { Description = "Lock status" }; updateLockedOption.DefaultValueFactory = _ => false; + var updateMesReportableOption = new Option("--mes-reportable") { Description = "Report this alarm to MES (IsFlaggedForMES on the alarm-status API)" }; + updateMesReportableOption.DefaultValueFactory = _ => false; // --trigger-kind for update (same semantics as add) var updateTriggerKindOption = new Option("--trigger-kind") { @@ -777,6 +783,7 @@ public static class TemplateCommands updateCmd.Add(updateDescOption); updateCmd.Add(updateTriggerConfigOption); updateCmd.Add(updateLockedOption); + updateCmd.Add(updateMesReportableOption); updateCmd.Add(updateTriggerKindOption); updateCmd.SetAction(async (ParseResult result) => { @@ -794,7 +801,8 @@ public static class TemplateCommands result.GetValue(updatePriorityOption)!, result.GetValue(updateDescOption), triggerConfig, - result.GetValue(updateLockedOption))); + result.GetValue(updateLockedOption), + result.GetValue(updateMesReportableOption))); }); group.Add(updateCmd); diff --git a/src/ZB.MOM.WW.ScadaBridge.CLI/README.md b/src/ZB.MOM.WW.ScadaBridge.CLI/README.md index 3a0ccd87..334a6a27 100644 --- a/src/ZB.MOM.WW.ScadaBridge.CLI/README.md +++ b/src/ZB.MOM.WW.ScadaBridge.CLI/README.md @@ -265,7 +265,7 @@ scadabridge --url template attribute delete --id Add an alarm definition to a template. ```sh -scadabridge --url template alarm add --template-id --name --trigger-type --priority [--description ] [--trigger-config ] [--locked] [--trigger-kind ] [typed-setpoint-flags] +scadabridge --url template alarm add --template-id --name --trigger-type --priority [--description ] [--trigger-config ] [--locked] [--mes-reportable] [--trigger-kind ] [typed-setpoint-flags] ``` | Option | Required | Description | @@ -277,6 +277,7 @@ scadabridge --url template alarm add --template-id --name - | `--description` | no | Description | | `--trigger-config` | no | Trigger configuration as a JSON string (overrides typed flags when both are supplied) | | `--locked` | no | Lock the alarm in derived templates | +| `--mes-reportable` | no | Report this alarm to MES (`IsFlaggedForMES` on the alarm-status API). Defaults to `false` | | `--trigger-kind` | no | Expression-trigger analysis kind: `advisory` (default — non-blocking advisory finding on a blank expression) or `strict` (blank expression is a deploy-blocking error). Ignored for non-`Expression` trigger types. | | `--attribute` | no | Attribute name the trigger watches (all trigger types except `Expression`) | | `--match-value` | no | `ValueMatch`: value to compare against | @@ -298,7 +299,7 @@ Update an alarm definition on a template. An update **replaces** the whole entit required field below must be supplied with its post-update value, even if unchanged. ```sh -scadabridge --url template alarm update --id --name --trigger-type --priority [--description ] [--trigger-config ] [--locked] [--trigger-kind ] +scadabridge --url template alarm update --id --name --trigger-type --priority [--description ] [--trigger-config ] [--locked] [--mes-reportable] [--trigger-kind ] ``` | Option | Required | Description | @@ -309,7 +310,8 @@ scadabridge --url template alarm update --id --name --trigg | `--priority` | yes | Alarm priority (0–1000) | | `--description` | no | Description | | `--trigger-config` | no | Trigger configuration JSON | -| `--locked` | no | Lock the alarm in derived templates | +| `--locked` | no | Lock the alarm in derived templates. Since `update` replaces the whole entity, omitting `--locked` resets it to `false` | +| `--mes-reportable` | no | Report this alarm to MES (`IsFlaggedForMES` on the alarm-status API). Since `update` replaces the whole entity, omitting `--mes-reportable` resets it to `false` | | `--trigger-kind` | no | Expression-trigger analysis kind: `advisory` (default) or `strict`. Ignored for non-`Expression` trigger types. **Requires `--trigger-config`** — the kind is written into the trigger-config JSON, so on `update` it has no effect without one (the CLI prints a warning to stderr and continues). | #### `template alarm delete` @@ -810,7 +812,7 @@ Label,"Hello, World", Set (upsert) an alarm override on an instance. ```sh -scadabridge --url instance alarm-override set --instance-id --alarm [--trigger-config ] [--priority ] +scadabridge --url instance alarm-override set --instance-id --alarm [--trigger-config ] [--priority ] [--mes-reportable ] ``` | Option | Required | Description | @@ -819,6 +821,7 @@ scadabridge --url instance alarm-override set --instance-id --alarm | `--alarm` | yes | Alarm canonical name (e.g. `TempLevels` or `Pump.TempSensor.Heat`) | | `--trigger-config` | no | JSON override for TriggerConfiguration (HiLo: partial merge; others: whole-replace) | | `--priority` | no | Priority override (0–1000) | +| `--mes-reportable` | no | MES-reportable override: `true` or `false`; omit to inherit the template value | #### `instance alarm-override delete` diff --git a/tests/ZB.MOM.WW.ScadaBridge.CLI.Tests/EditingParityTests.cs b/tests/ZB.MOM.WW.ScadaBridge.CLI.Tests/EditingParityTests.cs index 4329d85f..b15cbe9e 100644 --- a/tests/ZB.MOM.WW.ScadaBridge.CLI.Tests/EditingParityTests.cs +++ b/tests/ZB.MOM.WW.ScadaBridge.CLI.Tests/EditingParityTests.cs @@ -33,6 +33,10 @@ public class EditingParityTests [InlineData("instance attribute-override delete --instance-id 6 --attribute Module.Value", "DeleteInstanceAttributeOverride", "{\"instanceId\":6,\"attributeName\":\"Module.Value\"}")] [InlineData("instance native-alarm-source list --instance-id 6", "ListInstanceNativeAlarmSourceOverrides", "{\"instanceId\":6}")] [InlineData("instance alarm-override list --instance-id 6", "ListInstanceAlarmOverrides", "{\"instanceId\":6}")] + [InlineData("template alarm add --template-id 12 --name HighTemp --trigger-type HiLo --priority 500 --mes-reportable", "AddTemplateAlarm", "{\"templateId\":12,\"name\":\"HighTemp\",\"triggerType\":\"HiLo\",\"priorityLevel\":500,\"description\":null,\"triggerConfiguration\":null,\"isLocked\":false,\"isMesReportable\":true}")] + [InlineData("template alarm update --id 9 --name HighTemp --trigger-type HiLo --priority 500", "UpdateTemplateAlarm", "{\"alarmId\":9,\"name\":\"HighTemp\",\"triggerType\":\"HiLo\",\"priorityLevel\":500,\"description\":null,\"triggerConfiguration\":null,\"isLocked\":false,\"isMesReportable\":false}")] + [InlineData("instance alarm-override set --instance-id 6 --alarm HighTemp --mes-reportable false", "SetInstanceAlarmOverride", "{\"instanceId\":6,\"alarmCanonicalName\":\"HighTemp\",\"triggerConfigurationOverride\":null,\"priorityLevelOverride\":null,\"isMesReportableOverride\":false}")] + [InlineData("instance alarm-override set --instance-id 6 --alarm HighTemp --priority 7", "SetInstanceAlarmOverride", "{\"instanceId\":6,\"alarmCanonicalName\":\"HighTemp\",\"triggerConfigurationOverride\":null,\"priorityLevelOverride\":7,\"isMesReportableOverride\":null}")] [InlineData("data-connection move --id 4 --site-id 7", "MoveDataConnection", "{\"dataConnectionId\":4,\"targetSiteId\":7}")] public async Task Command_SendsExactManagementEnvelope(string arguments, string command, string payload) {