Skip to main content
Version: 2.6

TCP Data Access

ProcessorInfo

Declares the script which processes requests and received data. The script file is located at ...\<ProjectName>\Plugins\Drivers\HumanOS.UHAL.TcpClientControl\<ScriptName>

DataAccessInfo

Declare Data Nodes if needed.

Example:

{
"Name": "TestServer",
"Id": "6ae9da3f-4606-4c78-9eb3-aa70cebcb571",
"DriverId": "82AD96A4-5E4C-4FA6-B302-219CD92731BD",
"Address": "localhost:9999",
"Properties": [
{
"Name": "SendTimeout",
"Value": 4000,
"DataType": "System.Int32"
},
{
"Name": "ReceiveTimeout",
"Value": 5000,
"DataType": "System.Int32"
}
],
"DataNodes": [
{
"Id": "d21496a5-ab45-497c-aeab-4974ccd3a773",
"Name": "Example Data",
"DataClass": "Event",
"DataType": "System.Int32"
}
],
"TaskProcessors": [
{
"Id": "00159de8-45e2-4780-b5ff-4a26b3672477",
"Name": "MainTaskProcessor",
"ScriptFile": "TTestPayload.cs"
}
]
}

Script

The script depends as needed, following an example for SOAP HTTP requests and responses:

using CyberTech.Diagnostics;
using HumanOS.Kernel;
using HumanOS.Kernel.Communication;
using HumanOS.Kernel.DataModel;
using HumanOS.Kernel.UHAL.Device;
using HumanOS.Kernel.UHAL.Script;
using HumanOS.Kernel.Utils;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Xml.Linq;
using System.Xml.XPath;

namespace HumanOS.UHAL.TcpClient.Script
{
/// <summary>
/// Implements a payload processor
/// </summary>
public class ProcessPayload : TAbstractStreamScriptObject
{
///<see cref="TAbstractStreamScriptObject"/>
public override void handleStream(IKernelAccess Kernel, ILogger Logger, TDeviceInfo DeviceInfo, IDataStream DataStream)
{
List<TUHALDataNodeInfo> lstDataNodes = new List<TUHALDataNodeInfo>();
string strRequestBody = composeRequest(DeviceInfo, lstDataNodes);
//build the header
StringBuilder Builder = new StringBuilder();
Builder.AppendLine("POST / HTTP/1.1");
Builder.AppendLine(@"Content-Type: text/xml");
Builder.AppendLine(@"Connection: Keep-Alive");
Builder.AppendLine(string.Format("Content-Length: {0}", strRequestBody.Length));
Builder.AppendLine();

//add body and send
Builder.Append(strRequestBody);
Logger.writeDebug(Builder.ToString());
byte[] aui8Payload = Encoding.GetEncoding("ISO-8859-1").GetBytes(Builder.ToString());
writeToStream(DataStream, Logger, aui8Payload);

//wait for answer
string strResponse = readFromStream(DataStream, Logger);

if (strResponse.Length > 0)
{
MessageId++;
int iOffset = strResponse.IndexOf(@"Body");
setContent(strResponse.Substring(iOffset, strResponse.Length - iOffset), lstDataNodes, Kernel, Logger);
}
else
{
setNodeStates(EDataState.BadNotActive, lstDataNodes, Kernel, Logger);
}

//cooldown
Thread.Sleep(1000);
}

/// <summary>
/// Reads data from the stream - the stream is released by the client on disconnect
/// <returns></returns>
/// </summary>
private string readFromStream(IDataStream DataStream, ILogger Logger)
{
bool bOK = true;
string strResponse = "";

//read package
byte[] aui8ReadBuffer = new byte[100000];
int iStreamLength = DataStream.read(aui8ReadBuffer, 0, 100000);

if (iStreamLength == 0)
{
throw new IOException("Received 0 bytes, connection most certainly lost due to partner disconnect");
}
//min 20 max 100000
else if (iStreamLength > 20 && iStreamLength < 100000)
{
//Logger.writeDebug("Buffer read [" + iStreamLength + " bytes]");
}
else
{
bOK = false;
Logger.writeInfo("Received " + iStreamLength + " bytes which is larger or less than the limits: min=20, max=99999; ignoring payload...");
}

//everything OK so far
if (bOK)
{
strResponse = Encoding.GetEncoding("ISO-8859-1").GetString(aui8ReadBuffer);
Logger.writeDebug(strResponse);

//check for header OK
if (int.Parse(strResponse.Substring(strResponse.IndexOf(@"HTTP/1.1") + 8, 4)) == 200)
{
int iContentLength = int.Parse(strResponse.Substring(strResponse.IndexOf(@"Content-Length:") + 16, 5));
Logger.writeDebug("Content Length: " + iContentLength);

//check if we already received the whole package
if (!(iStreamLength >= iStreamLength + iContentLength))
{
//min 4 max 1000000
if (iContentLength > 4 && iContentLength < 1000000)
{
int iBytesReadTotal = 0;
aui8ReadBuffer = new byte[iContentLength];
do
{
int iBytesRead = DataStream.read(aui8ReadBuffer, iBytesReadTotal, iContentLength - iBytesReadTotal);
iBytesReadTotal += iBytesRead;
} while (iBytesReadTotal < iContentLength);

strResponse = Encoding.GetEncoding("ISO-8859-1").GetString(aui8ReadBuffer);
Logger.writeDebug(strResponse);
}
else
{
strResponse = "";
bOK = false;
Logger.writeInfo("Received " + iContentLength + " bytes which is larger or less than the limits: min=4, max=1000000; ignoring payload...");
}
}
}
else
{
strResponse = "";
bOK = false;
}
}

DataStream.flush();
return strResponse;
}

/// <summary>
/// Writes data to the stream - the stream is released by the client on disconnect
/// <param name="aui8WriteBuffer">data to write in bytes</param>
/// </summary>
private void writeToStream(IDataStream DataStream, ILogger Logger, byte[] aui8Payload)
{
DataStream.write(aui8Payload, 0, aui8Payload.Length);
DataStream.flush();
//Logger.writeDebug("Buffer wrote [" + aui8Payload.Length + " bytes]");
}

/// <summary>
/// Gets or sets id of request handle
/// </summary>
public int MessageId { get; set; }

/// <summary>
/// Sets a value to the data node
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="DataAccessInfo"></param>
/// <param name="strValue"></param>
/// <param name="Kernel"></param>
protected void setValueToNode<T>(TDataAccessInfo DataAccessInfo, string strValue, IKernelAccess Kernel)
{
IDataNode<T> nDataNode;
if (Kernel.NodeSpace.tryGetNode<IDataNode<T>>(DataAccessInfo.DataNodeId, out nDataNode))
{
nDataNode.passState(EDataState.Good);
nDataNode.passValue(TValueConverter.convertToObject<T>(strValue), false);
}
}

/// <summary>
/// Sets a state to the data node
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="DataAccessInfo"></param>
/// <param name="strValue"></param>
/// <param name="Kernel"></param>
protected void setStateToNode<T>(TDataAccessInfo DataAccessInfo, EDataState eState, IKernelAccess Kernel)
{
IDataNode<T> nDataNode;
if (Kernel.NodeSpace.tryGetNode<IDataNode<T>>(DataAccessInfo.DataNodeId, out nDataNode))
{
nDataNode.passState(eState);
}
}

/// <summary>
/// Composes a request body
/// </summary>
/// <param name="DeviceInfo"></param>
/// <returns></returns>
private string composeRequest(TDeviceInfo DeviceInfo, List<TUHALDataNodeInfo> lstDataNodes)
{
StringBuilder Items = new StringBuilder();

foreach (TUHALDataNodeInfo DataNodeInfo in DeviceInfo.DataNodes)
{
lstDataNodes.Add(DataNodeInfo);
}

//Body here

return strBase;
}

/// <summary>
/// Sets the content to the data nodes
/// </summary>
/// <param name="strContent"></param>
/// <param name="Kernel"></param>
/// <param name="DeviceInfo"></param>
private void setContent(string strContent, List<TDataAccessInfo> lstDataNodes, IKernelAccess Kernel, ILogger Logger)
{
//Parse payload accordingly
int iStartPos = strContent.IndexOf("<ItemList>");
int iEndPos = strContent.IndexOf("</ItemList>");
string strItems = strContent.getSubString(iStartPos, iEndPos + 10);

int iIndex = 0;
XElement ItemListNode = XElement.Parse(strItems.Replace("xsi:", ""));
foreach (XElement ItemNode in ItemListNode.XPathSelectElements("./Item"))
{
if (iIndex < lstDataNodes.Count)
{
TDataAccessInfo DataAccessInfo = lstDataNodes[iIndex];
try
{
MethodInfo Method = GetType().GetMethod("setValueToNode", BindingFlags.Instance | BindingFlags.NonPublic).MakeGenericMethod(DataAccessInfo.DataSystemType);
Method.Invoke(this, new object[]
{
DataAccessInfo,
ItemNode.XPathSelectElement("./Value").Value,
Kernel
});
}
catch (TargetInvocationException Exc)
{
Logger.writeError("Could not set value to node'" + DataAccessInfo.Name + "'.", Exc.InnerException != null ? Exc.InnerException : Exc);
}
}
iIndex++;
}
}

/// <summary>
/// Sets the node states
/// </summary>
/// <param name="eState"></param>
/// <param name="lstDataNodes"></param>
/// <param name="Kernel"></param>
private void setNodeStates(EDataState eState, List<TDataAccessInfo> lstDataNodes, IKernelAccess Kernel, ILogger Logger)
{
foreach (TDataAccessInfo DataAccessInfo in lstDataNodes)
{
try
{
MethodInfo Method = GetType().GetMethod("setStateToNode", BindingFlags.Instance | BindingFlags.NonPublic).MakeGenericMethod(DataAccessInfo.DataSystemType);
Method.Invoke(this, new object[] { DataAccessInfo, eState, Kernel });
}
catch (TargetInvocationException Exc)
{
Logger.writeError("Could not set state to node'" + DataAccessInfo.Name + "'.", Exc.InnerException != null ? Exc.InnerException : Exc);
}
}
}
}
}