Skip to main content
Version: 2.6

Serial Device Data Access

Processor Information

The payload processing must be declared in a processor information within the device info file.

{
"TaskProcessors": [
{
"Id": "DABEA01B-CDA0-4DCD-9135-0FE1C17A46AE",
"Name": "MainTaskProcessor",
"ScriptFile": "TTestPayload.cs"
}
]
}

The payload script is typically placed in the folder:

$(ConfigPath)\HumanOS.UHAL.SerialDeviceDriver

Scripting

The script file implements the protocol specific reading and writing operations. The complete communication must be implemented here.

The script is placed in the folder:

$(ConfigPath)\HumanOS.UHAL.SerialDeviceDriver\

The script object must be derived from TAbstractStreamScriptObject. Following methods can be overwritten:

MethodAction
handleStreamHandles the stream in a cyclic manner. This method MUST be implemented. Arguments: (Kernel: HumanOS® Kernel access; Logger: Logger instance of the driver; DeviceInfo: device info file; DataStream: interface to serial com-port)
connectToDeviceThis method is called after successful connection to the device. Place the code for initialization and connection preamble.Arguments: Same as handleStream()
disconnectFromDeviceThis method is called after successful disconnection from the device. Place the code for clean-up. Arguments: Same as handleStream()
onDataReceivedCallback, when data arrives on the serial com interface. Arguments: (Same as handleStream(); iBytesToRead: Number of bytes arrived in the read buffer)
write<T>Method is called if a value is written to a data node from someone else. Arguments: (Kernel: HumanOS® Kernel access; Logger: Logger instance of the driverData; Info: info node of data node; DataStream: interface to serial com-port; Value: data value of type <T>)

Example

Simple Example, reading and writing data over COM:

  /// <summary>
/// Test payload for script
/// </summary>
public class TTestPayload : TAbstractStreamScriptObject
{
///<see cref="TAbstractStreamScriptObject"/>
public override void handleStream(IKernelAccess Kernel,
ILogger Logger,
TDeviceInfo DeviceInfo,
IDataStream DataStream)
{
byte[] aui8Buffer = new byte[4];
DataStream.read(aui8Buffer, 0, 4);

aui8Buffer[0] = 0x11;
aui8Buffer[1] = 0x21;
aui8Buffer[2] = 0x31;
aui8Buffer[3] = 0x41;

DataStream.write(aui8Buffer, 0, 4);
}
}

Advanced example reading data from the device and set the corresponding data nodes, and vis versa; getting data from the node space and writing them to the device.

using CyberTech.Threading;
using CyberTech.Diagnostics;
using HumanOS.Kernel;
using HumanOS.Kernel.Communication;
using HumanOS.Kernel.DataModel;
using HumanOS.Kernel.Processing;
using HumanOS.Kernel.Utils;
using HumanOS.Kernel.UHAL;
using HumanOS.Kernel.UHAL.Device;
using HumanOS.Kernel.UHAL.Script;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using HumanOS.Kernel.DataModel.Space;

namespace Example.Payload
{
/// <summary>
/// Implements a test payload processor
/// </summary>
public class ProcessPayload : TAbstractStreamScriptObject
{

///<see cref="TAbstractStreamScriptObject"/>
public override EProcessingState connectToDevice(IKernelAccess Kernel, ILogger Logger, TDeviceInfo DeviceInfo, IDataStream DataStream)
{
return EProcessingState.Good;
}

///<see cref="TAbstractStreamScriptObject"/>
public override void disconnectFromDevice(IKernelAccess Kernel, ILogger Logger, TDeviceInfo DeviceInfo, IDataStream DataStream)
{

}


///<see cref="TAbstractStreamScriptObject"/>
public override void onDataReceived(IKernelAccess Kernel,
ILogger Logger,
TDeviceInfo DeviceInfo,
IDataStream DataStream,
int iBytesToRead)
{
byte[] aui8Buffer = new byte[iBytesToRead];
DataStream.read(aui8Buffer, 0, iBytesToRead);

string strText = System.Text.Encoding.ASCII.GetString(aui8Buffer);
string[] astrParts = strText.Split('@');

/////////////////////////////////////////////////
lock (m_DataLock)
{
int iLastIndex = m_lstContentToProcess.Count - 1;
if (m_lstContentToProcess.Count > 0)
{
m_lstContentToProcess[iLastIndex].Append(astrParts[0]);
}

for (int i = 1; i < astrParts.Length; i++)
{
m_lstContentToProcess.Add(new StringBuilder(astrParts[i]));
}

while (m_lstContentToProcess.Count > 2)
{
m_lstContentToProcess.RemoveAt(0);
}
}
/////////////////////////////////////////////////
}

///<see cref="TAbstractStreamScriptObject"/>
public override void handleStream(IKernelAccess Kernel, ILogger Logger, TDeviceInfo DeviceInfo, IDataStream DataStream)
{
string strData = "";
/////////////////////////////////////////////////
lock (m_DataLock)
{
if (m_lstContentToProcess.Count > 1)
{
strData = m_lstContentToProcess[0].ToString();
m_lstContentToProcess.RemoveAt(0);
}
}
/////////////////////////////////////////////////

if (!strData.isEmpty())
{
try
{
JObject Payload = JObject.Parse(strData);
string strTemperature = (string)Payload["Temperature"];
string strSwitch1 = (string)Payload["Switch1"];
string strSwitch2 = (string)Payload["Switch2"];
string strSwitch3 = (string)Payload["Switch3"];

setValue(Kernel.NodeSpace, Logger, DeviceInfo.DataNodes.First(n => n.Name == "Temperature"), strTemperature);
setValue(Kernel.NodeSpace, Logger, DeviceInfo.DataNodes.First(n => n.Name == "Switch1"), strSwitch1);
setValue(Kernel.NodeSpace, Logger, DeviceInfo.DataNodes.First(n => n.Name == "Switch2"), strSwitch2);
setValue(Kernel.NodeSpace, Logger, DeviceInfo.DataNodes.First(n => n.Name == "Switch3"), strSwitch3);
}
catch (JsonReaderException)
{
Logger.writeDebug($"JSON Error parsing the payload. Dismiss packet.");
}
catch (Exception Exc)
{
Logger.writeError($"Error parsing the payload. {Exc.Message}\n{Exc.StackTrace}");
}
}

StringBuilder DataToWrite = new StringBuilder();

bool bDataToProcess;
/////////////////////////////////////////////////
lock (m_DataLock)
{
bDataToProcess = !m_dicValuesToWrite.isEmpty();
bool bFirst = true;
foreach (KeyValuePair<string, string> Item in m_dicValuesToWrite)
{
if (!bFirst)
{
DataToWrite.Append(",");
}
bFirst = false;
DataToWrite.Append($"\"{Item.Key}\" : {Item.Value}");
}
m_dicValuesToWrite.Clear();
}
/////////////////////////////////////////////////

if (bDataToProcess)
{
//Write data to the device
string strDataToWrite = $"{{{DataToWrite}}}";
byte[] aui8Data = Encoding.ASCII.GetBytes(strDataToWrite);
DataStream.write(aui8Data, 0, aui8Data.Length);
DataStream.flush();
}
}

///<see cref="TAbstractStreamScriptObject"/>
public override void write<T>(IKernelAccess Kernel, ILogger Logger, TDataAccessInfo DataInfo, IDataStream DataStream, T Value)
{
if (Kernel.NodeSpace.tryGetNode<IDataNode<T>>(DataInfo.DataNodeId, out IDataNode<T> DataNode))
{
DataNode.passState(EDataState.Good);
DataNode.passValue(Value, false);

/////////////////////////////////////////////////
lock (m_DataLock)
{
m_dicValuesToWrite[DataNode.Name] = TValueConverter.convertToString(typeof(T), Value);
}
/////////////////////////////////////////////////
}
}

/// <summary>
/// Sets the data node value
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="NodeSpace"></param>
/// <param name="NodeId"></param>
/// <param name="Value"></param>
protected void setValueToDataNode<T>(INodeSpace NodeSpace, ILogger Logger, Guid NodeId, T Value)
{
if (NodeSpace.tryGetNode<IDataNode<T>>(NodeId, out IDataNode<T> DataNode))
{
DataNode.passValue(Value, false);
DataNode.passState(EDataState.Good);
}
else
{
Logger.writeWarning($"Node '{NodeId}' not configured.");
}
}

/// <summary>
/// sets a value to a data node
/// </summary>
/// <param name="NodeSpace"></param>
/// <param name="Logger"></param>
/// <param name="DataInfo"></param>
/// <param name="strValue"></param>
private void setValue(INodeSpace NodeSpace, ILogger Logger, TDataAccessInfo DataInfo, string strValue)
{
MethodInfo Method = this.GetType().GetMethod(nameof(setValueToDataNode), BindingFlags.NonPublic | BindingFlags.Instance).MakeGenericMethod(DataInfo.DataSystemType);
Method.Invoke(this, new object[] { NodeSpace, Logger, DataInfo.DataNodeId, TValueConverter.convertToObject(DataInfo.DataSystemType, strValue) });
}

/// <summary>
/// Data lock
/// </summary>
private readonly object m_DataLock = new object();

/// <summary>
/// read content to process
/// </summary>
private readonly List<StringBuilder> m_lstContentToProcess = new List<StringBuilder>();

/// <summary>
/// Write content to process
/// </summary>
private readonly Dictionary<string, string> m_dicValuesToWrite = new Dictionary<string, string>();

}
}