-
-
Notifications
You must be signed in to change notification settings - Fork 0
Setup: Data Model Configuration
Setting up an adapter using a data model may be more comfortable for programmers familiar with Object-Oriented programming or those who use frameworks such as Entity Framework. A data model configuration is best utilized when paired with an implementation of IAdapterSource
when you are consuming data from another system.
In this example, we'll create an IAdapterSource
that randomly updates values on a timer and call it TimerSource
. Implementing IAdapterSource
requires the following:
-
OnDataReceived
event -
Start()
method -
Stop()
method
class TimerSource : IAdapterSource {
public event DataReceivedHandler? OnDataReceived;
public void Start() { }
public void Stop() { }
}
Let's add the timer now:
class TimerSource : IAdapterSource {
// Implemented from IAdapterSource
public event DataReceivedHandler? OnDataReceived;
private System.Timers.Timer _timer = new System.Timers.Timer();
public TimerSource() {
_timer.Interval = 50;// Update every 50ms
_timer.Elapsed += _tick;
}
private void _tick(object? sender, System.Timers.ElapsedEventArgs e) {
// ... update DataItems
if (OnDataReceived != null) OnDataReceived(null, new DataReceivedEventArgs());
}
// Implemented from IAdapterSource
public void Start(CancellationToken token = default) {
_timer.Start();
}
// Implemented from IAdapterSource
public void Stop() {
_timer.Stop();
}
}
Now that we have a mechanism for "getting data", we need a data model to store it within. When designing a data model, you must use the DataItemAttribute
implementations on your properties.
class DataModel : IAdapterDataModel {
[Event("avail")]
public string? Availability { get; set; }
[Sample("xPos")]
public int? XPosition { get; set; }
[Sample("yPos")]
public int? YPosition { get; set; }
[Sample("zPos")]
public int? ZPosition { get; set; }
[Event("exec")]
public string? Execution { get; set; }
}
Going back into your IAdapterSource
implementation, let's now implement the use of your new DataModel
class:
class TimerSource : IAdapterSource {
// ... IAdapterSource implementation
public DataModel Model { get; private set; } = new DataModel();
private Random _rand = new Random();
private void _tick(object? sender, System.Timers.ElapsedEventArgs e) {
// Simply update the Model with the latest values
Model.Availability = "AVAILABLE";
Model.Execution = Guid.NewGuid().ToString();
Model.XPosition = _rand.NextDouble() * 1000;
Model.YPosition = _rand.NextDouble() * 1000;
Model.ZPosition = _rand.NextDouble() * 1000;
// Then, send the Model (updated or not) to the Adapter via the OnDataReceived event
if (OnDataReceived != null) OnDataReceived(Model, new DataReceivedEventArgs());
}
}
With both the IAdapterSource
and IAdapterDataModel
implemented, we can properly initialize our Adapter:
var source = new TimerSource();
var adapter = new TcpAdapter();
adapter.Start(source); // Provide an IAdapterSource to automatically map the DataItems
Console.WriteLine("Press any key to exit");
while(!Console.KeyAvailable) {
// Wait for input to exit
}
adapter.Stop();
See the PCAdapter
project for a full implementation of IAdapterSource
and IAdapterDataModel
. The PCAdapter project tracks your mouse movement as the data source.
With the release of v2.0.0, the Adapter SDK provides a comprehensive object model that mirrors the MTConnect standard. When configuring an IAdapterDataModel
with the use of implemented MTConnect Component classes, the Adapter SDK is capable of constructing the device model configuration for the reference C++ Agent and sending it to the Agent.
If you utilize the comprehensive object model for Observational Types/Sub-Types, then the references for those types are automatically generated in the device model configuration as well.
A simple example is when defining the axes for your machine:
public class MyMachineAxis : Axis
{
[Sample("pos")]
public Position.ACTUAL ActualPosition { get; set; }
}
public class MyMachineAxes : Axes
{
[DataItemPartial("x_")]
public MyMachineAxis X => GetOrAddAxis<MyMachineAxis>(nameof(X));
[DataItemPartial("y_")]
public MyMachineAxis Y => GetOrAddAxis<MyMachineAxis>(nameof(Y));
[DataItemPartial("z_")]
public MyMachineAxis Z => GetOrAddAxis<MyMachineAxis>(nameof(Z));
}
Resulting device model configuration fragment:
<Axes id="{name}" name="{name}">
<Components>
<Axis id="x" name="X">
<DataItems>
<DataItem category="SAMPLE" id="x_pos" name="x_pos" subType="ACTUAL" type="POSITION" units="MILLIMETER" />
</DataItems>
</Axis>
<Axis id="y" name="Y">
<DataItems>
<DataItem category="SAMPLE" id="y_pos" name="y_pos" subType="ACTUAL" type="POSITION" units="MILLIMETER" />
</DataItems>
</Axis>
<Axis id="z" name="Z">
<DataItems>
<DataItem category="SAMPLE" id="z_pos" name="z_pos" subType="ACTUAL" type="POSITION" units="MILLIMETER" />
</DataItems>
</Axis>
</Components>
</Axes>