HART communication : IHart,Hart, Nivotrack changes, sending/receiving works OK. Todo: Interface the level, ver. 2.18.1044

This commit is contained in:
Milan Hanajik
2018-10-25 15:34:48 +02:00
parent 3725e238fc
commit 845c66b511
11 changed files with 338 additions and 271 deletions
+109 -97
View File
@@ -16,39 +16,49 @@ using TBF.Boxes;
namespace TBF.BenchControl.Hart.Common
{
/// <summary>
/// Root component for Modbus communication via serial port (RS485)
/// Root component for HART communication via a modem connected to a serial port (RS232)
/// </summary>
public class Hart : ComponentBase, IDevice, GenericDevices.IModbus
public class Hart : ComponentBase, IDevice, GenericDevices.IHart
{
private static readonly ILog log = LogManager.GetLogger(typeof(Hart));
public override string ToString() { return string.Format("Modbus({0})", Cfg.ToString(1)); }
private readonly HartCfg modbusCommonCfg;
private readonly HartCfg hartCfg;
/// Private fields
SerialPort serialPort;
DateTime lastSerialPortWrite;
/// <summary>
/// An array of Queue-s of received telegrams fetched by HART components (as Nivotrack).
/// There is one queue for each device (each slave address).
/// </summary>
public Queue<byte[]>[] ReceivedTelegrams
{
get { return receivedTelegrams; }
}
Queue<byte[]>[] receivedTelegrams;
/// <summary>
/// A Queue of telegrams to send. System prevents sending more then one telegram per 500ms.
/// Function SendMessage( ) either send the telegram immediately using SendMessageNow( ) or
/// inserts the telegram into telegramsToSend queue. The queue is emptied in RunDeviceAfter( ).
/// </summary>
Queue<byte[]> telegramsToSend;
public Hart()
{
}
/// <summary>
/// Ambient temperature / humidity / pressure meter 'Greco' connected via serial interface (RS232)
/// Connection settings: 19200 Bd 8-bits No-parity 1-stop-bit Flow control: none or hardware.
/// HART modem connected via serial interface (RS232).
/// Connection settings: 1200 Bd 8-bits odd-parity 1-stop-bit Flow control: none or hardware.
/// </summary>
public Hart(Generic.IComponentCfg cfg)
: base(cfg)
{
receivedTelegrams = new Queue<byte[]>[256];
receivedTelegrams = new Queue<byte[]>[16]; /// Max. 16 slaves with adresses 0..15
for (int i = 0; i < receivedTelegrams.Length; i++)
{
receivedTelegrams[i] = new Queue<byte[]>();
@@ -56,7 +66,7 @@ namespace TBF.BenchControl.Hart.Common
telegramsToSend = new Queue<byte[]>();
serialPort = null;
modbusCommonCfg = cfg as HartCfg;
hartCfg = cfg as HartCfg;
log.Debug(this.ToString());
}
@@ -64,27 +74,62 @@ namespace TBF.BenchControl.Hart.Common
{
}
public void Initialize()
{
if (modbusCommonCfg.DebugLevel == DebugMode.Simulate)
if (hartCfg.DebugLevel == DebugMode.Simulate)
{
serialPort = null;
log.FatalFormat("{0} - Device simulated", Name);
return;
}
string comPortName = "COM" + modbusCommonCfg.ComPortNr.ToString();
serialPort = new SerialPort(comPortName, modbusCommonCfg.BaudRate, modbusCommonCfg.Parity, modbusCommonCfg.DataBits, modbusCommonCfg.StopBits);
serialPort.Handshake = modbusCommonCfg.Handshake;
string comPortName = "COM" + hartCfg.ComPortNr.ToString();
serialPort = new SerialPort(comPortName, hartCfg.BaudRate, hartCfg.Parity, hartCfg.DataBits, hartCfg.StopBits);
serialPort.Handshake = hartCfg.Handshake;
serialPort.Open();
//stopWorkerThread = false;
//workerThread = new Thread(Worker);
//workerThread.Start();
log.FatalFormat("{0} - Device successfully initialized", Name);
}
public void RunDeviceBefore()
{
if (serialPort != null)
{
ParseReceivedData();
}
}
public void RunDeviceAfter()
{
if ((serialPort != null) && (telegramsToSend.Count > 0) && (DateTime.Now - lastSerialPortWrite) > new TimeSpan(0, 0, 0, 0, 500))
{
SendMessageNow(telegramsToSend.Dequeue());
}
}
public void StopDevice()
{
if (serialPort != null)
{
serialPort.Close();
serialPort = null;
}
}
public void StopDevice2() { }
public void SendMessageNow(byte[] message)
{
serialPort.Write(message, 0, message.Length);
lastSerialPortWrite = DateTime.Now;
string s = Telegram.LogTelegram(string.Format("{0} - Sending message ", Name), message);
Debug.WriteLine(s);
log.Debug(s);
}
/// <summary>
/// Send an arbitrary modbus message.
@@ -92,21 +137,25 @@ namespace TBF.BenchControl.Hart.Common
/// The last two message bytes (CRC) may be uninitialized or zero.
/// They are calculated inside this function as required by Modbus specification.
/// </summary>
/// <param name="message">Message incl CRC fields, CRC bytes dont have to be set</param>
public void SendMessage(byte[] message)
/// <param name="message">Message incl. a reserved checksum byte, checksum does not have to be set</param>
public void SendMessage(int preambLen, byte[] message)
{
if (serialPort == null) return;
Telegram.UpdateTelegramCRC(message);
Telegram.UpdateTelegramChecksum(message);
byte[] preambAndMessage = new byte[message.Length + preambLen];
if ((DateTime.Now - lastSerialPortWrite) > new TimeSpan(0, 0, 0, 0, 100))
for (int i = 0; i < preambLen; i++) preambAndMessage[i] = (byte)0xFF;
for (int i = preambLen; i < preambLen + message.Length; i++) preambAndMessage[i] = message[i - preambLen];
if ((DateTime.Now - lastSerialPortWrite) > new TimeSpan(0, 0, 0, 0, 500))
{
/// More then 100 ms since last 'send' --> do not enqueue the message
SendMessageNow(message);
SendMessageNow(preambAndMessage);
}
else
{
telegramsToSend.Enqueue(message);
telegramsToSend.Enqueue(preambAndMessage);
string s = Telegram.LogTelegram(string.Format("{0} - Enqueueing message ", Name), message);
Debug.WriteLine(s);
@@ -114,98 +163,61 @@ namespace TBF.BenchControl.Hart.Common
}
}
/// <summary>
/// Send a structured modbus message.
/// </summary>
/// <param name="modbusAddress">Device address (1..255) or 0 = broadcast</param>
/// <param name="function">Function (0..127)</param>
/// <param name="dataAddress">Address of data to be transferred (0..65535)</param>
/// <param name="dataCount">Count of data bytes to be transferred (0..65535)</param>
public void SendMessage(byte modbusAddress, byte function, ushort dataAddress, ushort dataCount)
void ParseReceivedData()
{
if (serialPort == null) return;
byte[] message = new byte[8]
{
modbusAddress,
function,
(byte)(dataAddress / 256),
(byte)(dataAddress % 256),
(byte)(dataCount / 256),
(byte)(dataCount % 256),
0,
0,
};
SendMessage(message);
}
public void SendMessageNow(byte[] message)
{
serialPort.Write(message, 0, message.Length);
lastSerialPortWrite = DateTime.Now;
string s = Telegram.LogTelegram(string.Format("{0} - Sending message ", Name), message);
Debug.WriteLine(s);
log.Debug(s);
}
/// <summary>Run this device</summary>
public void RunDeviceBefore()
{
if (serialPort == null) return;
int nrBytes = serialPort.BytesToRead;
if (nrBytes > 0)
{
byte[] buffer = new byte[nrBytes];
serialPort.Read(buffer, 0, nrBytes);
/// Read available data
byte[] buf = new byte[nrBytes];
serialPort.Read(buf, 0, nrBytes);
int deviceAddress = (nrBytes >= 1) ? buffer[0] : 0;
int function = (nrBytes >= 2) ? buffer[1] : 0;
if ((nrBytes >= 4) && Telegram.VerifyTelegramCRC(buffer))
/// Determine count of leading FF-s
int preambLen = 0;
for (int i = 0; i < nrBytes; i++)
{
receivedTelegrams[deviceAddress].Enqueue(buffer);
if (buf[i] != (byte)0xFF) break;
preambLen++;
}
string s = string.Format("{0} - {1} address={2} count={3}", Name, Telegram.LogTelegram("Telegram received: ", buffer), deviceAddress, receivedTelegrams[deviceAddress].Count);
int telegramLen = nrBytes - preambLen;
/// Check a telegram length, a checksum, a start byte (S -> M) and a master address (primary)
if (preambLen < 3 ||
telegramLen < 4 ||
!Telegram.VerifyTelegramChecksum(preambLen, buf) ||
((buf[preambLen] != (byte)Mark.ShortFrameSlave2Master) && (buf[preambLen] != (byte)Mark.LongFrameSlave2Master)) ||
((buf[preambLen + 1] & 0x80) != (byte)Master.Primary))
{
string s = Telegram.LogTelegram(string.Format("{0} - Invalid data received ", Name), buf);
Debug.WriteLine(s);
log.Debug(s);
return;
}
/// Strip the preamble
byte[] data = new byte[telegramLen];
for (int i = preambLen; i < nrBytes; i++) data[i - preambLen] = buf[i];
bool isShortFrame = (data[0] == 0x06);
if (isShortFrame)
{
int address = data[1] & 0x0F;
receivedTelegrams[address].Enqueue(data);
string s = string.Format("{0} - {1} address={2} preambLen={3} count={4}", Name, Telegram.LogTelegram("Valid telegram received: ", data), address, preambLen, receivedTelegrams[address].Count);
Debug.WriteLine(s);
log.Debug(s);
}
else
{
string s = Telegram.LogTelegram(string.Format("{0} - Invalid data received ", Name), buffer);
/// TODO
string s = Telegram.LogTelegram(string.Format("{0} - Telegram with long address received ", Name), buf);
Debug.WriteLine(s);
log.Debug(s);
}
}
}
/// <summary>Run this device</summary>
public void RunDeviceAfter()
{
if (serialPort == null) return;
if (telegramsToSend.Count > 0 && (DateTime.Now - lastSerialPortWrite) > new TimeSpan(0, 0, 0, 0, 500))
{
SendMessageNow(telegramsToSend.Dequeue());
}
}
/// <summary>Stop this device</summary>
public void StopDevice()
{
if (serialPort != null)
{
//stopWorkerThread = true;
//workerThread.Join(2000);
serialPort.Close();
serialPort = null;
}
}
public void StopDevice2() { }
}
}