namespace ZB.MOM.WW.OtOpcUa.Driver.MTConnect; /// /// Result of an Agent /probe request: the full device hierarchy the Agent exposes. /// Produced by . This is the neutral shape both /// the TrakHound-backed implementation and a hand-rolled XML fallback must produce — nothing /// downstream of ever sees a TrakHound type or an /// XElement. /// /// Every device the Agent's probe response declares. public sealed record MTConnectProbeModel(IReadOnlyList Devices); /// /// Common shape shared by and : /// each may directly own data items and/or nest further components, to arbitrary depth. Backs /// the recursive walk. /// public interface IMTConnectComponentContainer { /// Components nested directly under this device/component (may be empty). IReadOnlyList Components { get; } /// Data items declared directly on this device/component (may be empty). IReadOnlyList DataItems { get; } } /// /// One MTConnect device from the Agent's probe response — the root of a nested /// component/data-item tree. /// /// The device's id attribute. /// The device's name attribute, when the Agent supplies one. /// Child components declared directly under this device. /// Data items declared directly on this device (outside any component). public sealed record MTConnectDevice( string Id, string? Name, IReadOnlyList Components, IReadOnlyList DataItems) : IMTConnectComponentContainer; /// /// One MTConnect component (e.g. Axes, Controller, Path) from the Agent's /// probe response. Components nest to arbitrary depth — a component may itself contain further /// child components as well as data items. /// /// The component's id attribute. /// The component's name attribute, when the Agent supplies one. /// Child components nested directly under this component. /// Data items declared directly on this component. public sealed record MTConnectComponent( string Id, string? Name, IReadOnlyList Components, IReadOnlyList DataItems) : IMTConnectComponentContainer; /// /// One MTConnect DataItem declaration from the Agent's probe response. This is pure metadata — /// it carries no observed value; values arrive separately via /// / /// and are correlated back to a data item by . /// /// /// The DataItem's id attribute — globally unique within the Agent's probe response and /// the correlation key used against . /// /// The DataItem's name attribute, when the Agent supplies one. /// The DataItem's category attribute — SAMPLE, EVENT, or CONDITION. /// The DataItem's type attribute (e.g. POSITION, EXECUTION). /// The DataItem's subType attribute (e.g. ACTUAL, COMMANDED), when present. /// The DataItem's units attribute (e.g. MILLIMETER, REVOLUTION/MINUTE), when present. /// /// The DataItem's representation attribute (e.g. TIME_SERIES, DISCRETE); /// absent means the MTConnect default (VALUE). /// /// /// The DataItem's sampleCount attribute — element count for a TIME_SERIES /// representation. Present only on data items that carry one. /// public sealed record MTConnectDataItem( string Id, string? Name, string Category, string Type, string? SubType, string? Units, string? Representation, int? SampleCount); /// /// Result of an Agent /current request, or of a single multipart chunk from a /// /sample stream. Produced by and /// . /// /// /// The Agent's current instance id (its MTConnectStreams header instanceId). Changes /// whenever the Agent restarts or its underlying device model changes; the driver watches this /// for change to raise rediscovery rather than trusting a stale observation snapshot. /// /// /// The header nextSequence — the sequence number to resume a subsequent /// call from. /// /// /// The header firstSequence — the oldest sequence number still held in the Agent's /// circular buffer. Load-bearing for sequence-gap detection: a caller that requested /// from a sequence older than this chunk's has fallen out of /// the Agent's buffer and must re-baseline via . /// /// /// The observations carried in this snapshot/chunk, in Agent-supplied order. Never null; /// empty when the chunk carries no new observations (e.g. a heartbeat). /// public sealed record MTConnectStreamsResult( long InstanceId, long NextSequence, long FirstSequence, IReadOnlyList Observations); /// /// One observed value for a single DataItem, as reported in a /current snapshot or a /// /sample stream chunk. Deliberately dumb: is carried as the raw /// string the Agent reported (including the literal UNAVAILABLE) with no interpretation /// applied here — coercing UNAVAILABLE to a bad-quality status and converting the string /// to the tag's DriverDataType are the observation index's job, not this layer's. /// /// /// The reporting DataItem's id — correlates back to /// from the probe model. /// /// /// The observed value exactly as the Agent reported it, or the literal string /// "UNAVAILABLE" when the Agent has no current value for the data item. Never /// pre-parsed into a nullable or an enum at this layer. /// /// /// The observation's Agent-reported timestamp, normalized to UTC ( /// is always ). Becomes the OPC UA variable's SourceTimestamp. /// /// /// true when the Agent carried this observation's real content in child elements /// rather than as text — an MTConnect 2.0 DATA_SET / TABLE observation, whose /// content is a list of <Entry key="…"> elements. /// /// Why the flag exists, and why only the parser can set it. is /// the element's concatenated descendant text, so a two-entry data set reads as the single /// token "12" — keys discarded, values run together. The observation index cannot /// detect that after the fact: neither this record nor the tag definition carries the /// DataItem's representation, and no value-shape heuristic can work, because /// concatenated entries are indistinguishable from a legitimate space-bearing EVENT such as /// a Message reading "Coolant level low". The one reliable discriminator — /// that the observation element had element children — exists only while the XML is still /// XML. /// /// /// Deliberately a neutral fact about the wire shape, not a status: mapping it to a /// quality code is the observation index's job (it codes these /// BadNotSupported rather than publishing concatenated noise as Good). Defaults to /// false, the shape of every ordinary scalar observation. /// /// public sealed record MTConnectObservation( string DataItemId, string Value, DateTime TimestampUtc, bool IsStructured = false); /// /// Recursive helpers over the device/component tree. /// public static class MTConnectComponentTreeExtensions { /// /// Enumerates every data item owned by this device or component, plus every data item owned /// by any component nested beneath it, to arbitrary depth. Order is depth-first: a node's /// own data items first, then each child component's data items in turn. /// /// The device or component to walk. public static IEnumerable AllDataItems(this IMTConnectComponentContainer container) { foreach (var dataItem in container.DataItems) { yield return dataItem; } foreach (var child in container.Components) { foreach (var dataItem in child.AllDataItems()) { yield return dataItem; } } } }