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;
}
}
}
}