Files
ScadaBridge/docs/components/DelmiaNotifier.md
T

7.7 KiB

Delmia Notifier

Delmia Notifier (#27) is a compact console executable that DELMIA Apriso shells out to on each recipe or NC-program download. It POSTs the notification to the ScadaBridge Inbound API method DelmiaRecipeDownload and reports the result in the legacy WWNotifier YES / NO stdout contract. It is an external client tool, not a cluster component: it never joins the cluster and has no actors or database.

Overview

The project lives in src/ZB.MOM.WW.ScadaBridge.DelmiaNotifier/. Its csproj sets AssemblyName to WWNotifier, PublishAot to true and InvariantGlobalization to true, so the published WWNotifier.exe is a file-for-file replacement for the legacy tool. No RuntimeIdentifier is fixed in the project; the runtime artifact is produced with dotnet publish -c Release -r win-x64 (Native AOT does not cross-compile from macOS or Linux). appsettings.json is copied to the output directory.

Per invocation the call path is:

DELMIA → WWNotifier.exe → POST {baseUrl}/api/DelmiaRecipeDownload  (X-API-Key: <key>)
                        ← { "Result": true/false, "ResultText": "..." }
        ← stdout: YES | NO\n<reason>   ·   exit: 0 | -1

The spec for this component is the project README.md plus docs/plans/2026-06-26-delmia-recipe-notifier-design.md; there is no Component-DelmiaNotifier.md requirements document. The inbound method it targets is described in docs/api-specs/Delmia-Recipe-Download-API.md.

Key Concepts

Output contract

Reporter.Report writes the only stdout Delmia parses. Success writes YES\n and returns 0. Failure writes NO\n followed by the reason (no trailing newline) and returns -1. All diagnostics go to stderr and, optionally, a log file, never to stdout.

Pre-execution-only failover

Notifier.RunAsync walks the configured base URLs in order and moves to the next URL only when the previous attempt provably did not execute. The sender classifies each attempt as one of four AttemptKind values:

Kind Raised when Loop behaviour
ConnectFailed HttpRequestException with HttpRequestError.NameResolutionError, or an inner SocketException with ConnectionRefused Try the next URL
StandbyRejected HTTP 503 whose X-ScadaBridge-Standby header is exactly 1 Try the next URL
OutcomeUnknown Any other HttpRequestException, or a timeout (OperationCanceledException not caused by the caller's token) Stop; return NO with OUTCOME_UNKNOWN: the request may have been applied; verify before retrying.
Connected Any HTTP response not matched above Stop; the node's answer is final

For a Connected outcome with a 2xx status, Body.Result == true yields YES (reason is ResultText); a false or unparseable body yields NO with ResultText, or request rejected if that is empty. A non-2xx status yields NO with HTTP <status>. If every URL fails pre-execution the reason is all URLs unreachable: <last error>. A generic 503 without the standby marker is final.

API key resolution

ConfigLoader.ResolveApiKey prefers the SCADABRIDGE_API_KEY environment variable when non-blank, then ScadaBridge:ApiKey from appsettings.json; both are trimmed. The chosen source name is logged (environment (SCADABRIDGE_API_KEY) or appsettings.json (ScadaBridge:ApiKey)), never the key. The tracked appsettings.json must keep ApiKey as "".

Architecture

File Role
Program.cs Main: load config, parse args, resolve URLs and key, build the HttpClient with TimeoutSeconds, run Notifier, report. Any exception becomes NO + unexpected error: .... A private LoggingRecipeSender decorator logs one line per attempt.
ArgParser.cs Hand-rolled, reflection-free parser for the legacy flags.
ConfigLoader.cs Deserialises appsettings.json (next to the exe, AppContext.BaseDirectory) via the source-generated NotifierJsonContext; validates every BaseUrls entry; splits the list; resolves the key.
Notifier.cs The failover loop and NotifyResult mapping.
IRecipeSender.cs / HttpRecipeSender.cs The send seam and its HTTP implementation.
Reporter.cs Legacy stdout and exit-code mapping.
DiagLog.cs Best-effort UTC-timestamped diagnostics.
RecipeDownload.cs / RecipeDownloadResult.cs Request and response DTOs.

Command-line flags

Each flag takes exactly one value. A flag without a value, or an unknown flag, fails the parse. Parsing failures produce NO + reason and exit -1 with no HTTP attempt.

Short Long Required Payload field
-m --machine yes MachineCode
-d --downloadpath yes DownloadPath
-w --workorder yes WorkOrderNumber
-p --partnumber yes PartNumber
-s --seqop no JobStepNumber
-u --username no Username

HttpRecipeSender

The URL is baseUrl.TrimEnd('/') + "/api/DelmiaRecipeDownload". The JSON body is serialised through the source-generated context, the X-API-Key header is attached, and the response body is read and parsed only for 2xx statuses. A JsonException on a 2xx body yields a null body, which the loop treats as a failure.

DiagLog

Each line is yyyy-MM-ddTHH:mm:ss.fffZ <message>, written to stderr and appended to LogPath when set (relative paths resolve against the exe directory, directories are created on demand). File errors are swallowed so the YES/NO contract is unaffected.

Usage

$env:SCADABRIDGE_API_KEY = "sbk_..."
.\WWNotifier.exe -m Z28061 -d "C:\recipes\job.nc" -w WO12345 -p PN-7788 -s 0100 -u operator1

The README documents a Docker-rig verification recipe (docker/seed-delmia-sim.sh, docker/seed-delmia-api.sh, docker/verify-delmia-notifier.sh) covering active-first, standby-first, bad-key and failover cases.

Configuration

appsettings.json sits next to the executable; the section is ScadaBridge.

Key Default Description
BaseUrls none Comma-separated base URLs (no path). Each entry must be an absolute HTTP(S) URI with a host, or config load fails. Empty yields no BaseUrls configured.
TimeoutSeconds 30 Per-attempt HttpClient timeout.
LogPath none Optional diagnostic log file; omit for stderr only.
ApiKey none Fallback key source; SCADABRIDGE_API_KEY wins.

Dependencies & Interactions

  • Inbound API (#14) — hosts the DelmiaRecipeDownload method the notifier calls; the standby marker X-ScadaBridge-Standby: 1 on a 503 is a server-side contract the notifier relies on.
  • Site Runtime (#3) — the inbound method routes to the machine instance's script, which performs the Delmia receiver handshake.
  • The project has no project references to other ScadaBridge assemblies; the test project accesses internals through InternalsVisibleTo.

Troubleshooting

NO with API key not configured

Neither SCADABRIDGE_API_KEY nor ScadaBridge:ApiKey is non-blank. A stale environment variable shadows an edited file; check the api key source: log line.

NO with HTTP 401

The node answered, so the answer is final and no failover happens. Inbound API keys are a per-node store, so a key valid on one central may be absent on the other.

NO with OUTCOME_UNKNOWN

A timeout or non-refusal transport error. The download may have been applied; verify before retrying manually.

NO with config load failed

appsettings.json is missing or malformed, or a BaseUrls entry is not an absolute HTTP(S) URI.

  • Inbound API
  • Site Runtime
  • src/ZB.MOM.WW.ScadaBridge.DelmiaNotifier/README.md
  • docs/api-specs/Delmia-Recipe-Download-API.md