Files
ScadaBridge/docs/former-api-specs/mes/Alarm-API.md
T
Joseph Doherty 8a78e759c0 docs: former-api-specs (MES + DNC/Delmia) + inbound compile-error known issue
- 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
2026-06-26 04:13:19 -04:00

10 KiB
Raw Blame History

WWSupport MES API — Alarm Status API

Reference for the alarm-status endpoints exposed by the WWSupport / APIServer ServiceStack service. The API reads live machine-alarm state out of AVEVA Wonderware (System Platform / Galaxy) via MXAccess and returns it to MES callers.

  • Service host: AppHost (AppSelfHostBase, name "APIServer") — APIServer/APIServer/AppHost.cs
  • Service implementation: MesServicesAPIServer/APIServer.ServiceInterface/MesServices.cs
  • Business logic: MesNotifierAPIServer/APIServer.ServiceInterface/MesNotifier.cs
  • Framework: ServiceStack 6.0.2, self-hosted; SQL Server (OrmLite, SqlServer2016Dialect)

Serialization note: JsConfig.IncludeNullValues = true, so null fields are emitted in JSON responses (e.g. "AckDT": null).


Endpoints

Verb Route Request DTO Response DTO
POST /mes/alarmstatus AlarmStatusRequest AlarmStatusResponse
POST /mes/simplealarmstatus SimpleAlarmStatusRequest AlarmStatusResponse

Both endpoints return the same AlarmStatusResponse shape. Both are dispatched through ServiceStack Any(...) handlers in MesServices, which resolve the singleton MesNotifier and call AlarmStatus(...) / SimpleAlarmStatus(...).

The service also enables PostmanFeature and OpenApiFeature (Swagger), so a running instance exposes a browsable contract and a Postman collection.


Authentication & authorization

All MES services are decorated with:

[Authenticate]
[RequiredRole("MESAPI")]
public class MesServices : Service { ... }

The caller must be authenticated and hold the MESAPI role. Auth is configured in AppHost.Configure:

  • API keyApiKeyAuthProvider
    • SessionCacheDuration = 30 minutes
    • RequireSecureConnection = false (HTTP is accepted; TLS not enforced)
    • An API key whose Environment == "test" is routed to the TestDb connection instead of the production DB (AppHost.GetDbConnection).
  • LDAPLdapAuthProvider (directory credentials).
  • AllowGetAuthenticateRequests = true.

User/role data is persisted with OrmLiteAuthRepository (UseDistinctRoleTables = true) in the same SQL Server database.


POST /mes/simplealarmstatus

The convenience endpoint: identify a machine by SAPID, get back its MES-relevant alarms.

Request — SimpleAlarmStatusRequest

Field Type Notes
SAPID string SAP identifier of the machine. Required.
{ "SAPID": "100012345" }

Behavior

  1. Look up the Machine by SAPID. If not found → WasSuccessful = false, ErrorText = "Failed to find machine with SAPID '<id>'".
  2. Select that machine's alarms filtered to FlaggedForMES = true only.
  3. Read live tag state and return every alarm that is currently triggered (InAlarm == true).

Notes specific to this endpoint:

  • Always flagged-only (cannot return non-MES alarms).
  • Does not honor an "include acked" toggle — acked alarms are always included (with StatusCode = "Triggered.Acked").

POST /mes/alarmstatus

The full endpoint: pick the machine by any one of several keys, and filter the alarm set.

Request — AlarmStatusRequest

Field Type Notes
MachineFilter MachineFilter Selects the machine (see below). Defaults to empty.
AlarmFilter AlarmFilter Filters the alarms (see below). Defaults to "all flagged + unflagged, triggered + acked".

MachineFilter

Field Type Notes
MachineID int? DB primary key.
SAPID string SAP identifier.
ZTag string Z-tag identifier.
Code string Machine code (also the MXAccess tag prefix).

Resolution precedence (first non-empty wins): SAPIDCodeZTagMachineID. At least one selector must be supplied; if all are empty/unmatched the call fails with "Failed to find machine with given machine filter '<dump>'". A supplied-but-unmatched selector fails with a selector-specific message, e.g. "Failed to find machine with Code '<code>'".

AlarmFilter

Field Type Default Effect
NameFilter string null Case-insensitive substring match on alarm Name.
MinSeverity int? null Keep alarms with Severity >= MinSeverity.
MaxSeverity int? null Keep alarms with Severity <= MaxSeverity.
IncludeTriggered bool true Currently unused — see Behavior notes.
IncludeAcked bool true When false, acked alarms are excluded from the result.
FlaggedOnly bool false When true, restrict to FlaggedForMES = true alarms.
{
  "MachineFilter": { "SAPID": "100012345" },
  "AlarmFilter": {
    "MinSeverity": 500,
    "FlaggedOnly": true,
    "IncludeAcked": false
  }
}

Behavior

  1. Resolve the machine via MachineFilter precedence (above).
  2. Load all MachineAlarm rows for that machine, then apply the in-process AlarmFilter in this order: FlaggedOnlyMinSeverityMaxSeverityNameFilter.
  3. Read live tag state; return every remaining alarm that is triggered (InAlarm == true), skipping acked alarms when IncludeAcked == false.

Response — AlarmStatusResponse

Field Type Notes
WasSuccessful bool false on any lookup or tag-read failure.
ErrorText string Populated when WasSuccessful == false.
Alarms AlarmInfo[] Triggered alarms matching the request. Cleared if WasSuccessful == false.

AlarmInfo

Field Type Source
Name string MachineAlarm.Name.
HierarchicalName string "{Machine.Code}.{Name}".
Description string Live …​.DescAttrName tag value.
IsFlaggedForMES bool MachineAlarm.FlaggedForMES.
Severity int MachineAlarm.Severity (0999).
StatusCode string "Triggered", or "Triggered.Acked" when acked.
TriggeredDT DateTime Live …​.TimeAlarmOn tag value.
AckDT DateTime? Live …​.TimeAlarmAcked tag value (null if unacked).
AckComment string Live …​.AckMsg tag value.

Example response

{
  "WasSuccessful": true,
  "ErrorText": null,
  "Alarms": [
    {
      "Name": "HighVacuumFault",
      "HierarchicalName": "Z28061.HighVacuumFault",
      "Description": "High vacuum sensor out of range",
      "IsFlaggedForMES": true,
      "Severity": 800,
      "StatusCode": "Triggered.Acked",
      "TriggeredDT": "2026-06-25T01:14:22",
      "AckDT": "2026-06-25T01:16:05",
      "AckComment": "Investigating - day shift"
    }
  ]
}

How alarm state is read (MXAccess)

Alarm configuration (name, severity, MES flag) lives in SQL; live state is read from Wonderware at request time:

  • For each MachineAlarm, an AlarmTagset (AlarmTagset.cs) builds MXAccess tag paths of the form {Machine.Code}.{Alarm.Name}.<suffix>, where <suffix> is one of: Quality, InAlarm, TimeAlarmOn, DescAttrName, Acked, TimeAlarmAcked, AckMsg.
  • MesNotifier subscribes via LMXProxyServerClass.AdviseSupervisory (handle registered as "MesNotifier"), waits for the first data change per tag, then unsubscribes.
  • Quality gate: every alarm's Quality must be OPC-Good (192); otherwise the whole request fails with ErrorText = "Failed to read machine alarm status" and Alarms is cleared.
  • Detail tags (TimeAlarmOn, DescAttrName, Acked, TimeAlarmAcked, AckMsg) are only read for alarms whose InAlarm is true.
  • Per-request timeout: 30 seconds (CancellationTokenSource(30000)); on timeout the pending reads resolve as failed.

Underlying data model — MachineAlarm

SQL table backing alarm configuration (APIServer.ServiceModel/DTO/MachineAlarm.cs):

Column Type Constraints
MachineAlarmID int PK, auto-increment
MachineID int FK → Machine
Machine Machine OrmLite [Reference]
Name string Required, ≤ 256
Severity int 0999
FlaggedForMES bool Marks an alarm as MES-relevant
LastUpdate DateTime
LastUpdateBy string Required, ≤ 128
OtherData string Max-length text

Behavior notes & gotchas

  • AlarmFilter.IncludeTriggered is declared but never used. Both endpoints only ever return alarms whose InAlarm tag is true; setting IncludeTriggered = false has no effect.
  • Only triggered alarms are returned. There is no "all configured alarms" mode — an alarm not currently in alarm state never appears, regardless of filters.
  • /simplealarmstatus is flagged-only and ignores ack filtering; use /mes/alarmstatus with FlaggedOnly / IncludeAcked for control over those.
  • MachineFilter precedence matters — supplying both SAPID and Code uses SAPID; the others are ignored.
  • All-or-nothing on read errors. A single bad-quality tag fails the whole response (success flag flips, alarm list is emptied) rather than returning a partial set.
  • Severity bounds are inclusive on both MinSeverity and MaxSeverity.
  • The route attributes carry placeholder Swagger text (Summary = "POST Summary", Notes = "Notes"); these are cosmetic.

Quick reference (curl)

# Simple — flagged alarms for one machine by SAPID
curl -X POST http://<host>:<port>/mes/simplealarmstatus \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <api-key>" \
  -d '{"SAPID":"100012345"}'

# Full — filtered alarms, machine chosen by Code
curl -X POST http://<host>:<port>/mes/alarmstatus \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <api-key>" \
  -d '{
        "MachineFilter": { "Code": "Z28061" },
        "AlarmFilter": { "MinSeverity": 500, "IncludeAcked": false }
      }'

Exact auth header format depends on how ApiKeyAuthProvider is configured for the deployment (bearer token vs. HTTP Basic with the key as username). Confirm against the live Swagger/Postman metadata for the target server.