EtherCAT Diagnostics
Real-time EtherCAT fieldbus diagnostics — master state, slave health, error counters, and frame statistics. Exposes the same data as Beckhoff TE2000 HMI EtherCAT Diagnostics, but as a REST + SignalR API.
Feature flag: EtherCatDiagnostics (disabled by default)
Multi-Master Support
A PLC can have multiple EtherCAT masters. The API scopes all slave/sync-unit endpoints under a specific master.
GET /api/plcs/{plcId}/ethercat/masters List masters
GET /api/plcs/{plcId}/ethercat/masters/{masterId} Master detail
GET /api/plcs/{plcId}/ethercat/masters/{masterId}/slaves Slave list
GET /api/plcs/{plcId}/ethercat/masters/{masterId}/slaves/{address} Slave detail
POST /api/plcs/{plcId}/ethercat/masters/{masterId}/slaves/{address}/reset-error-counters
GET /api/plcs/{plcId}/ethercat/masters/{masterId}/syncunits Sync unitsList Masters
GET /api/plcs/{plcId}/ethercat/mastersReturns all EtherCAT masters discovered for this PLC. The master’s AMS NetId is derived from the PLC’s NetId (typically byte 5 differs, e.g., PLC=192.168.1.136.1.1 → master=192.168.1.136.3.1).
{
"data": [
{
"deviceId": 0,
"name": "EtherCAT Master 0",
"amsNetId": "192.168.1.136.3.1",
"currentState": "Op",
"requestedState": "Op",
"slaveCount": 3
}
]
}| Field | Description |
|---|---|
deviceId | Integer ID used in URL path ({masterId}) |
currentState | EtherCAT state: Init, PreOp, Bootstrap, SafeOp, Op |
slaveCount | Number of configured slaves |
Master Detail
GET /api/plcs/{plcId}/ethercat/masters/{masterId}Includes frame statistics.
{
"data": {
"deviceId": 0,
"name": "EtherCAT Master 0",
"amsNetId": "192.168.1.136.3.1",
"currentState": "Op",
"requestedState": "Op",
"slaveCount": 3,
"frameStatistics": {
"cyclicSendFrames": 2033684256,
"queuedSendFrames": 312674,
"cyclicLostFrames": 0,
"queuedLostFrames": 52167,
"cyclicFramesPerSecond": 0,
"queuedFramesPerSecond": 0,
"cyclicTxRxErrors": 0,
"queuedTxRxErrors": 0
}
}
}List Slaves
GET /api/plcs/{plcId}/ethercat/masters/{masterId}/slavesReturns all configured slaves with their state and device type. Device names are decoded from the Beckhoff product code (e.g., EK1100, EL2808).
{
"data": [
{
"physicalAddress": 1001,
"autoIncrementAddress": 0,
"name": "EK1100",
"type": "EK1100",
"currentState": "Op",
"requestedState": "Op",
"isPresent": true,
"hasError": false,
"isDisabled": false,
"identityMatch": true,
"crcErrorSum": 0
}
]
}| Field | Description |
|---|---|
physicalAddress | EtherCAT fixed address (used in URL as {address}) |
name | User-assigned name or decoded device type (e.g., EK1100); falls back to Slave {addr} for unknown vendors |
type | Hardware type derived from product code (e.g., EK1100, EL2808) — always reflects the physical device series |
currentState | EtherCAT state of this slave |
isPresent | Physically present on the bus |
hasError | Slave is signaling an error |
crcErrorSum | Total CRC errors across all ports |
Slave Detail
GET /api/plcs/{plcId}/ethercat/masters/{masterId}/slaves/{address}Full slave detail including device identity, error counters, and port diagnostics.
{
"data": {
"physicalAddress": 1001,
"name": "EK1100",
"type": "EK1100",
"configuredIdentity": {
"vendorId": 2,
"productCode": 72100946,
"revisionNumber": 1179648,
"serialNumber": 0
},
"scannedIdentity": { ... },
"currentState": "Op",
"isPresent": true,
"hasError": false,
"initError": false,
"errorCounters": {
"crcErrorSum": 0,
"abnormalStateChanges": 0,
"connectionLosses": 0
},
"ports": [
{
"port": "A",
"physic": "EBus",
"configured": true,
"linkState": true,
"crcErrorCount": 0,
"forwardedCrcErrors": 0,
"lostLinkCount": 0
}
]
}
}Beckhoff Product Code Decoding
For Beckhoff devices (vendor ID 2), the product code encodes the device type:
- Upper 16 bits = terminal number (e.g.,
0x044C= 1100) - Lower 16 bits = device family (
0x2C52= EK coupler,0x3052= EL terminal,0x4052= EP box)
Reset Error Counters
POST /api/plcs/{plcId}/ethercat/masters/{masterId}/slaves/{address}/reset-error-countersResets CRC and frame counters. Requires WriteAccess policy (operator/admin + ads:write scope).
Returns 204 No Content on success.
Real-Time Notifications (SignalR)
Hub: /api/plcs/{plcId}/ethercat/wsRequires ReadAccess policy. JWT via ?access_token= query string.
On connect, the client is automatically subscribed to all EtherCAT events for that PLC. Events are broadcast to all connected clients.
| Event | Payload | Description |
|---|---|---|
MasterStateChanged | { masterId, masterName, currentState, previousState, requestedState } | Master state transition |
SlaveStateChanged | { masterId, address, name, currentState, previousState, hasError } | Slave state change |
SlavePresenceChanged | { masterId, address, name, isPresent } | Slave appeared/disappeared |
CrcErrorThresholdExceeded | { masterId, address, name, port, crcCount, threshold } | CRC errors crossed threshold |
SyncUnitFault | { masterId, syncUnitId, hasError, faultCounter } | Sync unit WC fault |
Configuration
{
"PlcTargets": {
"Line1": {
"AmsNetId": "192.168.1.136.1.1",
"Port": 851,
"EtherCat": {
"PollingIntervalMs": 1000,
"CrcErrorThreshold": 100,
"EnableNotifications": true,
"TimeoutMs": 5000
}
}
}
}| Field | Description | Default |
|---|---|---|
PollingIntervalMs | How often to poll diagnostics data | 1000 |
CrcErrorThreshold | CRC count that triggers notification | 100 |
EnableNotifications | Enable SignalR events for this PLC | true |
TimeoutMs | ADS read timeout for EtherCAT operations | 5000 |
ADS Implementation Notes
EtherCAT diagnostics are read via ADS from the EtherCAT master device:
- Master NetId: derived from PLC NetId (byte 5 varies, e.g.,
.3.1) - Port 0xFFFF: all diagnostic reads target the master’s AMS NetId on port 65535
- Per-slave reads: use the slave’s fixed address as the IO offset (not ADS port)
- Connection: routes through the PLC’s TCP connection — no separate ADS route needed
Key ADS index groups on master port 0xFFFF:
| IG | IO | Description |
|---|---|---|
0x03 | 0x100 | Master state (uint16) |
0x06 | 0x0 | Slave count (uint16) |
0x07 | 0x0 | Slave address list (uint16[]) |
0x09 | 0x0 | All slave states (byte[] — 2 per slave) |
0x09 | slave addr | Per-slave state + link |
0x0C | 0x0 | Frame counters (5× uint32) |
0x11 | slave addr | Slave identity (4× uint32: vendor, product, revision, serial) |
0x12 | slave addr | CRC errors per port (4× uint32) |