Claude-Session: https://claude.ai/code/session_013L3XdmVii9i5sY4FChU8pB
7.8 KiB
DelmiaNotifier (WWNotifier.exe)
A compact, self-contained Windows console app that DELMIA Apriso shells out to on each recipe / NC-program download to notify the plant that a recipe was placed at a path for a machine.
It is the modern, drop-in replacement for the legacy WWNotifier (see
../../docs/former-api-specs/dnc/Delmia-Integration-API.md,
Surface B), repointed from the old Wonderware /notify receiver to the ScadaBridge Inbound API
method DelmiaRecipeDownload (as-deployed spec: docs/api-specs/Delmia-Recipe-Download-API.md). The command-line flags, the YES/NO stdout contract, and the
exit-code semantics are unchanged, so Delmia's call site and output parsing need no changes — only
the executable on disk and its appsettings.json are swapped.
The published assembly name is
WWNotifierprecisely so it is a literal file-for-file drop-in for Delmia's existing call path.
What it does
Per invocation it POSTs the recipe-download notification to the configured ScadaBridge node(s) and maps the result back to the legacy contract:
DELMIA → WWNotifier.exe → POST {baseUrl}/api/DelmiaRecipeDownload (X-API-Key: <key>)
← { "Result": true/false, "ResultText": "..." }
← stdout: YES | NO\n<reason> · exit: 0 | -1
Command-line flags
| Short | Long | Required | → payload field |
|---|---|---|---|
-d |
--downloadpath |
yes | DownloadPath |
-m |
--machine |
yes | MachineCode |
-w |
--workorder |
yes | WorkOrderNumber |
-p |
--partnumber |
yes | PartNumber |
-s |
--seqop |
no | JobStepNumber |
-u |
--username |
no | Username |
Each flag takes one value (-m Z28061). A missing required flag → NO + reason, exit -1, no
HTTP attempt.
WWNotifier.exe -m Z28061 -d "C:\recipes\job.nc" -w WO12345 -p PN-7788 -s 0100 -u operator1
Output contract (drop-in parity)
- stdout is reserved for the contract Delmia parses:
- success →
YES(exit0) - failure →
NOon the first line, then a human-readable reason on the next line (exit-1)
- success →
- exit code:
0on success,-1on failure. (On POSIX shells-1surfaces as255; on Windows%ERRORLEVEL%is-1.) - All diagnostics (per-URL attempt, status code, which URL answered, exceptions) go to stderr and,
if
LogPathis set, an appended log file — never to stdout.
Delivery semantics
The BaseUrls failover loop is at-least-once, not exactly-once: a connection reset after the
target already processed the POST, or a slow response that exceeds TimeoutSeconds, both look like
an unreachable node to this client and cause a re-POST to the next URL — even though the first
attempt may already have succeeded. The deployed DelmiaRecipeDownload inbound method must be
idempotent on (MachineCode, DownloadPath, WorkOrderNumber) so a duplicate delivery is harmless;
see the design doc's "Delivery semantics: at-least-once" section for details.
Configuration — appsettings.json
Placed next to the exe (copied to the output on build/publish):
{
"ScadaBridge": {
"BaseUrls": "http://host-a:8085,http://host-b:8085",
"TimeoutSeconds": 30,
"LogPath": "logs/delmia-notifier.log",
"ApiKey": ""
}
}
BaseUrls— comma-separated failover list of base URLs only; the app appends/api/DelmiaRecipeDownload. URLs are tried in order with connect-failure-only failover: a node that responds at all (even4xx/5xx) is authoritative and its answer is final; only a node that fails to connect (refused/reset, DNS, TLS, timeout) rolls over to the next. If every URL fails to connect →NO+ last connection error.TimeoutSeconds— per-attempt HTTP timeout (default30).LogPath— optional diagnostic log file, relative to the exe. Omit to log to stderr only.
Secret — SCADABRIDGE_API_KEY (or ScadaBridge:ApiKey)
The inbound API key is sent as X-API-Key. It is resolved from SCADABRIDGE_API_KEY first
(recommended for production — the key never touches disk), falling back to ScadaBridge:ApiKey in
appsettings.json when the variable is unset or blank (a blank file value counts as unset too; both
values are trimmed). The diagnostic log records which source supplied the key — never the key itself — so
a stale environment variable silently shadowing an edited file is visible. If neither is set → NO +
API key not configured (SCADABRIDGE_API_KEY or ScadaBridge:ApiKey), exit -1, no HTTP attempt.
Set the variable on the machine/account that DELMIA runs under (e.g. a system/user environment
variable, or the service account's environment). If you use the file instead, restrict its ACL to the
account DELMIA runs under — the key is stored in plain text there. Fill ApiKey in only on the deployed
copy: the repo's appsettings.json is tracked and copied to the build output, so a key pasted there to
test locally is one git add away from being committed. It must stay "" in source control.
Building & publishing (Native AOT, win-x64)
The runtime artifact is a self-contained, single-file native exe. Native AOT does not cross-compile from macOS/Linux — publish on Windows with the MSVC build tools (Desktop C++ workload) installed:
# On Windows:
dotnet publish src/ZB.MOM.WW.ScadaBridge.DelmiaNotifier -c Release -r win-x64
# → bin/Release/net10.0/win-x64/publish/WWNotifier.exe (+ appsettings.json)
Copy WWNotifier.exe and appsettings.json to the Delmia host, set SCADABRIDGE_API_KEY (or fill in
ScadaBridge:ApiKey on that copy), and
point BaseUrls at the central node(s).
Managed
dotnet build/dotnet testrun cross-platform and are the development/CI gate; only the final native publish requires Windows.
Manual smoke test
After deploying (or against a reachable test cluster such as wonder-app-vd03):
$env:SCADABRIDGE_API_KEY = "sbk_..."
.\WWNotifier.exe -m Z28061Sim -d "C:\recipes\test.nc" -w WO-TEST -p PN-TEST -s 0100 -u smoketest
# expect: YES (exit 0) — or NO + reason (exit -1) with details on stderr / in the log file
This mirrors the earlier curl verification of POST /api/DelmiaRecipeDownload with the
X-API-Key header.
Docker rig recipe (no Galaxy needed)
The local rig (docker/) carries an in-memory stand-in for a vd03-style machine, so the whole
path — notifier → inbound method → site Call script → simulated DelmiaReceiver handshake — runs
on a laptop, including a 2-node central failover. Verified 2026-09-02
(docs/api-specs/Delmia-Recipe-Download-API.md §8):
bash docker/seed-delmia-sim.sh # SimMachine/DelmiaReceiverSim templates + DelmiaSim01 on site-a, deployed
bash docker/seed-delmia-api.sh # DelmiaRecipeDownload method + scoped key; prints DELMIA_API_KEY=sbk_... ONCE
export DELMIA_API_KEY=sbk_...
bash docker/sync-inbound-api-keys.sh # keys are a per-node store: mirror central-a -> central-b (register row 108)
bash docker/verify-delmia-notifier.sh # runs the managed build from a scratch copy; the key goes in THAT copy's appsettings.json
| Case | Setup | Expected |
|---|---|---|
| A | active node first in BaseUrls |
YES |
| B | standby first (standby relays to the active) | YES |
| C | bad key | NO + HTTP 401 (an answer is final — no failover) |
| D | active docker killed, attempt inside the takeover window |
NO + HTTP 503 — expected: connect-failure-only failover, the survivor is not yet active |
| E | survivor /health/active = 200 (~27 s) |
YES (first URL refused → second URL) |
| F | victim restarted and rejoined as standby | YES (victim relays) |
| G | DownloadPath ending in .bad |
NO + Simulated reject: bad path or missing work order |