Skip to content

Expose the parsed probe device model cached by MTConnectHttpClient #176

@ottobolyos

Description

@ottobolyos

Summary

MTConnectHttpClient parses each /probe response into a fully wired IDevice tree and caches it in the private field _devices (libraries/MTConnect.NET-HTTP/Clients/MTConnectHttpClient.cs, line 25), but never surfaces it. A consumer that needs the post-probe device model—device names and UUIDs, and the component ancestry of each DataItem—has no public way to reach the wired objects the client already holds.

Use case

A consumer built on top of the client often needs the agent's structure, not just its observations: deriving an address or routing key for each DataItem from its place in the device tree, mirroring the structure into another system at connect time, or validating that an expected component exists before subscribing. Each of those needs, for every DataItem, the owning IDevice (.Name, .Uuid), the full IComponent ancestry from device root to the DataItem's parent, and the DataItem's own identity (.Id, .Name, .Type, .SubType, .Category).

What already exists internally

After parsing, every IDataItem in the cached tree carries fully wired back-pointers—IDataItem.Container, IDataItem.Device, and IContainer.Parent—set by the XML deserialiser (MTConnect.NET-XML/Devices/XmlDevice.cs, lines 115–116 and 142; XmlComponent.cs, lines 105–106 and 133). The ancestry a consumer needs is therefore already in memory; this is purely a question of access.

Related bug—DeviceReceived never fires

ProcessProbeDocument (…/MTConnectHttpClient.cs, line 827) builds an empty outputDevices list (line 838), populates only _devices in the first loop, then raises DeviceReceived by iterating outputDevices (lines 851–854)—which is never populated. So DeviceReceived is raised zero times today. ProbeReceived (line 857) is the only working probe signal, and it carries the raw IDevicesResponseDocument, before InstanceId stamping.

Suggested shape (bird's-eye)

Two small additions—no new parsing, network calls, or interface changes:

  1. A public read-only accessor over the existing cache, returning a lock-guarded snapshot:

    public IReadOnlyDictionary<string, IDevice> Devices { get { lock (_lock) { return new Dictionary<string, IDevice>(_devices); } } }
  2. Fix DeviceReceived to fire—raise it inside the first loop (or populate outputDevices before the second), so the event delivers the same post-processed IDevice the accessor would.

Together they give consumers both paths to the same wired tree: event-driven (DeviceReceived) and snapshot-on-demand (Devices). Both are read-throughs of state the client already maintains, so the change is small.

Workaround in the meantime

A consumer can subscribe to ProbeReceived and walk the raw IDevicesResponseDocument itself, reconstructing ancestry via IDataItem.Container and IContainer.Parent (the back-pointers are wired on the raw document too). It works, but duplicates traversal logic the client could expose directly, and re-walks the document on every probe.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions