Dahlke.EtherCAT.Diagnostics
0.11.0
dotnet add package Dahlke.EtherCAT.Diagnostics --version 0.11.0
NuGet\Install-Package Dahlke.EtherCAT.Diagnostics -Version 0.11.0
<PackageReference Include="Dahlke.EtherCAT.Diagnostics" Version="0.11.0" />
<PackageVersion Include="Dahlke.EtherCAT.Diagnostics" Version="0.11.0" />
<PackageReference Include="Dahlke.EtherCAT.Diagnostics" />
paket add Dahlke.EtherCAT.Diagnostics --version 0.11.0
#r "nuget: Dahlke.EtherCAT.Diagnostics, 0.11.0"
#:package Dahlke.EtherCAT.Diagnostics@0.11.0
#addin nuget:?package=Dahlke.EtherCAT.Diagnostics&version=0.11.0
#tool nuget:?package=Dahlke.EtherCAT.Diagnostics&version=0.11.0
Dahlke.EtherCAT.Diagnostics
EtherCAT master and slave diagnostics over TwinCAT ADS: topology, slave and port state, CRC and frame error counters, sync-unit faults, CoE object reads and writes, and a change-event stream.
It reads through raw ADS index groups rather than the symbol API, because that is the only way to reach an EtherCAT master — there are no PLC symbols for any of this. The raw channel comes from Dahlke.TwinCAT.Ads, so you get its connection pooling, reconnection and simulation for free.
dotnet add package Dahlke.EtherCAT.Diagnostics
Quick start
builder.Services.AddTwinCatAds(builder.Configuration); // the raw channel factory
builder.Services.AddEtherCatDiagnostics(); // client, cache, polling monitor
You must also supply two application concerns this library deliberately does not invent:
builder.Services.AddSingleton<IEtherCatOptionsSource, MyOptionsSource>(); // which masters to poll
builder.Services.AddSingleton<IEtherCatDiagnosticsHandler, MyHandler>(); // where events go
Then read the bus:
public sealed class TopologyEndpoint(IEtherCatClient client)
{
public async Task<string> DescribeAsync(CancellationToken ct)
{
var masters = await client.GetMastersAsync("192.168.1.10.1.1", ct);
var master = masters[0];
var state = await client.GetMasterStateAsync(master.AmsNetId, ct);
var slaves = await client.GetConfiguredSlavesAsync(master.AmsNetId, ct);
var frames = await client.GetFrameStatisticsAsync(master.AmsNetId, ct);
return $"{master.Name}: {state?.State}, {slaves?.Count} slaves, "
+ $"{frames?.CyclicLostFrames} cyclic frames lost";
}
}
What it gives you
IEtherCatClient |
One-shot reads: masters, master state, configured vs. scanned slaves, per-slave detail, error counters, sync units, CoE objects, and a CiA-402 drive's decoded statusword. Counters are resettable and CoE objects are writable. |
IEtherCatCache |
The last snapshot the monitor took, so a request path never has to touch the bus. |
IEtherCatMonitor |
The polling loop, registered as a hosted service. Re-arm a CRC notification with ClearCrcNotification. |
IEtherCatEvent |
The change stream — slave present/absent, slave and master state changes, CRC threshold exceeded, sync-unit fault, diagnostics degraded. |
Configured vs. scanned matters. GetConfiguredSlavesAsync returns what the project says should be on the bus; GetScannedSlavesAsync returns what is actually answering. The difference is the diagnosis.
Nullable returns are the contract, not an oversight. A master that is not reachable yields null rather than throwing, so a dashboard polling six masters is not taken down by one unplugged cable.
Parameterising a slave over CoE
ReadCoeObjectAsync and WriteCoeObjectAsync reach a slave's object dictionary. Both address it by ADS port (the slave's fixed address), not by the index offset the diagnostic reads use, and both are strictly on-demand — a slave with no mailbox never answers, so neither belongs in a polling loop.
var write = await client.WriteCoeObjectAsync(
master.AmsNetId, physicalAddress: 1004,
index: 0x8010, subIndex: 0x11,
data: BitConverter.GetBytes((ushort)2500), // little-endian, exactly as wide as the object
timeoutMs: 3000, ct);
if (!write.Succeeded && write.Reason == CoeFailureReason.SdoAbort)
logger.LogWarning("drive refused it: {Error}", write.Error); // e.g. SDO abort 0x06010002: …
A refusal is a result, not an exception, and the slave's own reason is the point. Reason separates a slave with no mailbox from an object that is not in its dictionary from an SdoAbort — and an abort carries the device's verbatim code in AbortCode (0x06010002 read-only object, 0x06090030 value out of range, 0x08000021 local control), with the ETG.1000-6 description in Error. During commissioning that code is the difference between "fix the value" and "put the drive back in remote".
Three things this deliberately does not do: it does not read the value back (a device may clamp, round, or store to a shadow copy pending a save to 0x1010), it does not encode CoE data types for you, and it does not suppress the raw channel's retry — so a write that gets no answer can reach the slave twice. That is safe for a parameter store and wrong for an object whose write is a command; set RawChannels:RetryCount to 0 in a host that writes those.
Reading a drive's CiA-402 state
ReadCia402StatusAsync reads object 0x6041 and hands back the decoded drive state instead of two bytes:
var status = await client.ReadCia402StatusAsync(
master.AmsNetId, physicalAddress: 1004, timeoutMs: 3000, ct);
if (status.Succeeded && status.Status!.Value.State == Cia402State.Fault)
logger.LogWarning("drive is faulted: 0x{Word:X4}", status.Statusword);
It is a convenience over ReadCoeObjectAsync and nothing more — same ADS-port addressing, same on-demand-only rule, same retry, and the CoE read's whole failure vocabulary (Reason, AbortCode, Error) forwarded untouched. The decoding itself lives in Dahlke.EtherCAT.Cia402, which this package depends on and which depends on nothing: use it directly if you already have the word.
Two cases worth knowing. A successful read can carry Cia402State.Unknown — that is the drive reporting a word the CiA-402 state table does not name, which is an answer, so read Statusword when it happens. And a slave that answers fewer than two bytes is reported as a failure rather than decoded, because inventing the high byte would fabricate the remote and target-reached flags.
Nothing here checks that the slave is a drive at all: 0x6041 on a non-drive either does not exist (ObjectNotFound, or an abort) or means something else, and reading 0x1000 first to find out would double the round trips for every caller.
Turning the polling off
AddEtherCatDiagnostics(startMonitor: false) registers everything but does not run the loop. There is no internal enable flag, and this is why: a REST controller is activated by its framework before whatever feature gate would have turned the request away, so its dependencies have to resolve either way. Registering without polling lets the surface exist while the bus is left alone. Not calling AddEtherCatDiagnostics at all is the other way to turn it off, and the right one when nothing references the library.
Testing without hardware
Because the transport is Dahlke.TwinCAT.Ads's raw channel, its simulation applies — seed raw index-group responses and exercise this library with no PLC and no TwinCAT installation. See the raw-channel simulation section of the repository README.
Links
- Source, issues and the other packages in this repository: https://github.com/patdhlk/Dahlke.TwinCAT.Ads
- Changelog: https://github.com/patdhlk/Dahlke.TwinCAT.Ads/blob/main/CHANGELOG.md
Apache-2.0.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Beckhoff.TwinCAT.Ads (>= 7.0.292 && < 8.0.0)
- Dahlke.EtherCAT.Cia402 (>= 0.11.0)
- Dahlke.EtherCAT.Esi (>= 0.11.0)
- Dahlke.TwinCAT.Ads (>= 0.11.0)
- Microsoft.Bcl.TimeProvider (>= 8.0.1)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.2)
- Microsoft.Extensions.Hosting.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.3)
- Microsoft.Extensions.Options (>= 8.0.0)
- System.Memory (>= 4.6.0)
-
net10.0
- Beckhoff.TwinCAT.Ads (>= 7.0.292 && < 8.0.0)
- Dahlke.EtherCAT.Cia402 (>= 0.11.0)
- Dahlke.EtherCAT.Esi (>= 0.11.0)
- Dahlke.TwinCAT.Ads (>= 0.11.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.9)
- Microsoft.Extensions.Hosting.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.9)
- Microsoft.Extensions.Options (>= 10.0.0)
-
net8.0
- Beckhoff.TwinCAT.Ads (>= 7.0.292 && < 8.0.0)
- Dahlke.EtherCAT.Cia402 (>= 0.11.0)
- Dahlke.EtherCAT.Esi (>= 0.11.0)
- Dahlke.TwinCAT.Ads (>= 0.11.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.2)
- Microsoft.Extensions.Hosting.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.3)
- Microsoft.Extensions.Options (>= 8.0.0)
-
net9.0
- Beckhoff.TwinCAT.Ads (>= 7.0.292 && < 8.0.0)
- Dahlke.EtherCAT.Cia402 (>= 0.11.0)
- Dahlke.EtherCAT.Esi (>= 0.11.0)
- Dahlke.TwinCAT.Ads (>= 0.11.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.0)
- Microsoft.Extensions.Hosting.Abstractions (>= 9.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.0)
- Microsoft.Extensions.Options (>= 9.0.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Dahlke.EtherCAT.Diagnostics:
| Package | Downloads |
|---|---|
|
OpenEC.Monitor.Ads
Optional read-only TwinCAT master (ADS) enrichment for the OpenEC.Monitor EtherCAT SDK. |
GitHub repositories
This package is not used by any popular GitHub repositories.