- former-api-specs/mes: Alarm-API, MoveIn-MoveOut-API, API-key authgaps (from ~/Desktop/mesapi) - former-api-specs/dnc: Delmia-Integration-API — Delmia document service + WW recipe-download notify (from ~/Desktop/delmiaintegration) - known-issues: inbound API compile error not client-visible; no api-method validate
11 KiB
Delmia / DNC Integration API — document download & WW recipe-download notification
Reference for the delmiaintegration solution (~/Desktop/delmiaintegration), the legacy
bridge that pulls "proven" manufacturing documents (NC programs / recipes) out of DELMIA Apriso
and notifies AVEVA Wonderware that a recipe was downloaded for a machine. The recipe/NC-program
push to machines is classic DNC (Distributed Numerical Control), hence this lives under
former-api-specs/dnc/.
There are two distinct API surfaces in this solution:
| # | Surface | Direction | Transport |
|---|---|---|---|
| A | Delmia document web service | integration → Delmia (consumed) | form-urlencoded POST, XML response |
| B | WW recipe-download notification | WWNotifier CLI → Wonderware receiver |
JSON POST (/notify) |
Surface B is the legacy predecessor of the ScadaBridge
DelmiaRecipeDownloadinbound method — same field set and result shape (see ScadaBridge equivalent below).
Source files (under ~/Desktop/delmiaintegration):
DelmiaIntegration/DelmiaClient.cs— Delmia HTTP client (Surface A)DelmiaContracts/*.cs— XML contracts (DownloadResult,SearchResults/SearchResult, + unusedMachineInfo/MachineSearchResults/UserInfo)WWNotifier/Program.cs,WWNotifier/CommandLineOptions.cs,WWNotifier/Models/RecipeDownload*.cs— the CLI notifier (Surface B)WWNotifier/App.config—NotifyURL/NotifyTimeout
End-to-end flow
DELMIA Apriso ──(A) RequestProvenDocument / RequestDocument / Search──► DelmiaClient
(intercim "ruleset" web service, XML) │ downloads proven doc/recipe to a path
▼
WWNotifier.exe ──(B) POST /notify {RecipeDownload}──► Wonderware "WW receiver"
(CLI, exit code + YES/NO) ◄── {RecipeDownloadResult} ──
Surface A — Delmia document web service (consumed by DelmiaClient)
DelmiaClient (DelmiaClient.cs) is a thin HttpClient wrapper. The base URL is supplied by
the caller (constructor / URL property — there is no default in DelmiaIntegration/App.config);
Timeout defaults to 30 s. Every call is an application/x-www-form-urlencoded POST to
{URL}/<Operation>, and every response is XML deserialized with XmlSerializer (root
namespace http://intercim.com/ruleset — the Apriso/InterCIM heritage).
| Verb | Path | Form fields | Response (XML) | Client method |
|---|---|---|---|---|
POST |
{URL}/RequestProvenDocument |
username, machineID, partNumber, operationNumber, workOrderNumber |
DownloadResult |
RequestProvenDocument[Async] |
POST |
{URL}/RequestDocument |
+ documentKey |
DownloadResult |
RequestDocument[Async] |
POST |
{URL}/Search |
username, machineID, partNumber, operationNumber |
SearchResults |
Search[Async] |
RequestProvenDocument— fetch the single proven (released/approved) document for the part + operation, logging it againstusername/workOrderNumber.Search— list candidate documents matching the part/operation (each carries aDocumentKey+DocumentURL).RequestDocument— fetch one specific document chosen from a search bydocumentKey.
username here is identity/audit only — it is a form field, not an authentication credential.
There is no API key, no Authorization header, no TLS requirement on this surface (see gotchas).
Response — DownloadResult (metadata about the download)
DownloadResult describes who/what/which order+document was downloaded — it does not carry
the file bytes (the file lands at a download path on the Delmia side). Fields:
| Group | Fields |
|---|---|
| User | UserKey (int), UserName, UserSite |
| Machine | MachineKey (int), MachineID, MachineSite |
| Order | WorkOrderNumber, ShopOrderKey (int), ShopOrderID, ShopOrderStatus, ShopOrderOperKey (int), ShopOrderOperID, ShopOrderOperStatus |
| Document | DocumentKey (int), DocumentName, DocumentRev, DocumentStatus |
| Part | PartID, PartRev |
| Outcome | TransferSuccessful (bool), ErrorMessage (string) |
Success is
TransferSuccessful, not the HTTP status. TreatTransferSuccessful == trueas the only success signal;ErrorMessagecarries the reason otherwise.
Response — SearchResults / SearchResult
SearchResults = { List<SearchResult> Results; string ErrorMessage }. Each SearchResult:
ShopOrderKey/ShopOrderID/ShopOrderStatus, ShopOrderOperKey/ShopOrderOperID/ShopOrderOperStatus,
DocumentKey (int), DocumentObjectID (int, with an XmlIgnore …Specified flag),
DocumentName, DocumentRev, DocumentStatus, DocumentURL, PartID, PartRev.
Quick reference (curl, Surface A)
# Search for candidate documents (values from DownloadTestUtil's example)
curl -X POST "{URL}/Search" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "username=dohertj2" \
--data-urlencode "machineID=000005" \
--data-urlencode "partNumber=00444455599" \
--data-urlencode "operationNumber=0100"
# -> <SearchResults xmlns="http://intercim.com/ruleset"> … </SearchResults>
# Request the proven document for that part/operation
curl -X POST "{URL}/RequestProvenDocument" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "username=dohertj2" \
--data-urlencode "machineID=000005" \
--data-urlencode "partNumber=00444455599" \
--data-urlencode "operationNumber=0100" \
--data-urlencode "workOrderNumber=W111111"
Surface B — WW recipe-download notification (WWNotifier → WW receiver)
WWNotifier.exe (WWNotifier/Program.cs) is a command-line tool invoked by the download
process to tell Wonderware that a recipe/NC file was placed at a path for a machine. It POSTs a
JSON RecipeDownload to the configured receiver and interprets the JSON RecipeDownloadResult.
CLI options (CommandLineOptions)
| Short | Long | Required | Meaning |
|---|---|---|---|
-d |
--downloadpath |
yes | File download path |
-m |
--machine |
yes | Machine code |
-w |
--workorder |
yes | Work order number |
-p |
--partnumber |
yes | Part / item number |
-s |
--seqop |
no | Job step / sequence number |
-u |
--username |
no | Operator username |
Endpoint & payload
POST {NotifyURL} with a JSON body, expecting a JSON reply. From WWNotifier/App.config:
NotifyURL = http://wonder-app-vd01.zmr.zimmer.com:9001/notify, NotifyTimeout = 30 (seconds).
NotifyURL may be a comma-separated list — each is tried in order and the first success
wins (failover).
Request — RecipeDownload (JSON)
| Field | Type | Source |
|---|---|---|
MachineCode |
string | --machine |
DownloadPath |
string | --downloadpath |
WorkOrderNumber |
string | --workorder |
PartNumber |
string | --partnumber |
JobStepNumber |
string | --seqop |
Username |
string | --username |
Response — RecipeDownloadResult (JSON): { "Result": bool, "ResultText": string }.
Process contract (stdout + exit code)
The caller (Delmia) reads WWNotifier's console output and exit code:
- On success → prints
YES; exit code0. - On any failure (arg parse, missing config, all receivers failed,
Result == false) → printsNOplus a reason line;Environment.ExitCode = -1.
WWNotifier.exe -m Z28061 -d "C:\recipes\wo111111.nc" -w W111111 -p P111111 -s 0100 -u chamalas
# stdout: YES (or: NO\n<reason>)
# POSTs {"MachineCode":"Z28061","DownloadPath":"C:\\recipes\\wo111111.nc", … } to /notify
Other contracts (defined but unused)
MachineInfo, MachineSearchResults ({ List<MachineInfo> Results; string ErrorMessage }), and
UserInfo exist in DelmiaContracts (same intercim.com/ruleset namespace) but are not wired to
any DelmiaClient operation — scaffolding for machine-lookup / user-lookup endpoints that were
never implemented. Shapes, for reference: MachineInfo = MachineKey/MachineID/MachineName/
DownloadPath/MachineDescription/MachineSite/MachineStatus; UserInfo = UserKey/UserName/
UserSite/IsActive.
Behavior notes & gotchas
- No authentication on either surface. Surface A's
usernameis just a logged form field; Surface B sends no credential at all. Both are plaintext HTTP. (Contrast the ScadaBridge inbound API, which requiresX-API-Key/Bearer— see../mes/authgaps.md.) - Surface A ignores HTTP status.
DelmiaClientnever callsEnsureSuccessStatusCode; a non-2xx body is handed straight toXmlSerializer, which typically throws → thecatchreturns a genericTransferSuccessful = false/"Failed to call Delmia web service at '<URL>'.". The real HTTP error is lost. - Sync methods block on
.Result.RequestProvenDocument/RequestDocument/Searchcall.Resulton the async POST (deadlock-prone in some contexts); async variants exist. WWNotifierhas a latent NPE in its error path. On a notify exception it logserror.InnerException.Message(Program.cs:129); ifInnerExceptionis null this throws inside thecatch, masking the original error.NotifyURLfailover is first-success-wins, in list order; a slow first endpoint costs up toNotifyTimeoutbefore the next is tried.- Surface A base URL is caller-supplied (no config default), so the effective Delmia endpoint
depends on whoever constructs
DelmiaClient(e.g.TestUI/DelmiaClientUI).
ScadaBridge equivalent (porting note)
- Surface B → ScadaBridge Inbound API
DelmiaRecipeDownload. The legacyWWNotifier.exe+ the/notifyWW receiver are replaced byPOST /api/DelmiaRecipeDownload(authenticated withX-API-Key/Bearer). The contract is identical: request{ MachineCode, DownloadPath, WorkOrderNumber, PartNumber, JobStepNumber, Username }→ response{ Result, ResultText }. The inbound script routes to the site viaRoute.To(MachineCode).Call("ProcessRecipeDownload", …). The CLI's comma-list failover is superseded by Traefik active-node routing; theYES/NO+ exit code contract becomes the HTTP status + JSON body. - Surface A (Delmia document service) has no direct ScadaBridge equivalent — retrieving proven documents from DELMIA Apriso remains an external concern (it would be an External System Gateway call if pulled into ScadaBridge).
This file documents the legacy delmiaintegration contracts for reference/parity during that
migration.