TBF : KPackE.Radio, KPackE.DataEntryForRadio, KPackE.RegisterReader and IReceivesDataFromRadio added, ver. 2.18.1115
This commit is contained in:
@@ -0,0 +1,288 @@
|
||||
///
|
||||
/// Copyright (c) 2018 Sensus Slovensko a.s.
|
||||
///
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO.Ports;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Config.Entities;
|
||||
using log4net;
|
||||
using TBF.BenchControl.Generic;
|
||||
using TBF.Boxes;
|
||||
using TBF.BenchControl.GenericDevices;
|
||||
|
||||
namespace TBF.BenchControl.RegisterReaders.KPackE.Radio
|
||||
{
|
||||
/// <summary>
|
||||
/// Root component for Modbus communication via serial port (RS485)
|
||||
/// </summary>
|
||||
public class Radio : ComponentBase, IDevice
|
||||
{
|
||||
private static readonly ILog log = LogManager.GetLogger(typeof(Radio));
|
||||
public override string ToString() { return string.Format("Radio({0})", Cfg.ToString(1)); }
|
||||
|
||||
enum KPackE_Type
|
||||
{
|
||||
Reading = 0,
|
||||
RSSI = 1,
|
||||
Delta = 2,
|
||||
SmartMeterEvent = 5,
|
||||
Event = 7,
|
||||
}
|
||||
|
||||
enum KPackE_Original
|
||||
{
|
||||
Repeated = 0,
|
||||
Original = 1,
|
||||
}
|
||||
|
||||
///
|
||||
/// Definition of telegrams and telegram frames
|
||||
///
|
||||
static readonly byte[] Ack = new byte[] { 0x06 };
|
||||
static readonly byte[] HartBeatTelegram = new byte[] { 0x0A, 0x30, 0x30, 0x20, 0x4F, 0x4B, 0x41, 0x59, 0x20, 0x40, 0x0D }; // <LF>00 OKAY @<CR> (11 bytes)
|
||||
static readonly byte[] KPackETelegramFrame = new byte[] { 0x02, 0x0F, 0x4D, 0x45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x03, 0, 0x0A, 0x0D };
|
||||
static readonly int MinRcvdTlgrmLen = Math.Min(HartBeatTelegram.Length, KPackETelegramFrame.Length);
|
||||
static readonly int MaxRcvdTlgrmLen = Math.Max(HartBeatTelegram.Length, KPackETelegramFrame.Length);
|
||||
|
||||
///
|
||||
/// Private fields
|
||||
///
|
||||
private readonly RadioCfg radioCfg;
|
||||
|
||||
SerialPort serialPort;
|
||||
DateTime lastSerialPortWrite;
|
||||
DateTime lastTelegramReceived;
|
||||
IList<byte> data; /// Received and not yet processed data
|
||||
|
||||
/// <summary>
|
||||
/// Returns true when the last telegram is not older then 60 seconds
|
||||
/// </summary>
|
||||
public bool IsRadioOn()
|
||||
{
|
||||
return (DateTime.Now - lastTelegramReceived) < new TimeSpan(0, 0, 60);
|
||||
}
|
||||
|
||||
|
||||
public IList<RegisterReader.RegisterReader> RegReaders;
|
||||
public IReceivesDataFromRadio EntryForm;
|
||||
|
||||
|
||||
public Radio()
|
||||
{
|
||||
}
|
||||
|
||||
/// <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.
|
||||
/// </summary>
|
||||
public Radio(Generic.IComponentCfg cfg)
|
||||
: base(cfg)
|
||||
{
|
||||
RegReaders = new List<RegisterReader.RegisterReader>();
|
||||
|
||||
serialPort = null;
|
||||
radioCfg = cfg as RadioCfg;
|
||||
data = new List<byte>();
|
||||
|
||||
log.Debug(this.ToString());
|
||||
}
|
||||
|
||||
~Radio()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
if (radioCfg.DebugLevel == DebugMode.Simulate)
|
||||
{
|
||||
serialPort = null;
|
||||
log.FatalFormat("{0} - Device simulated", Name);
|
||||
return;
|
||||
}
|
||||
|
||||
lastTelegramReceived = DateTime.MinValue;
|
||||
|
||||
string comPortName = "COM" + radioCfg.ComPortNr.ToString();
|
||||
serialPort = new SerialPort(comPortName, radioCfg.BaudRate, radioCfg.Parity, radioCfg.DataBits, radioCfg.StopBits);
|
||||
serialPort.Handshake = radioCfg.Handshake;
|
||||
serialPort.Open();
|
||||
|
||||
//stopWorkerThread = false;
|
||||
//workerThread = new Thread(Worker);
|
||||
//workerThread.Start();
|
||||
|
||||
log.FatalFormat("{0} - Device successfully initialized", Name);
|
||||
}
|
||||
|
||||
|
||||
public void SendAcknowledgeNow()
|
||||
{
|
||||
serialPort.Write(Ack, 0, Ack.Length);
|
||||
lastSerialPortWrite = DateTime.Now;
|
||||
|
||||
string s = Telegram.LogTelegram(string.Format("{0} - Sending message ", Name), Ack);
|
||||
Debug.WriteLine(s);
|
||||
log.Debug(s);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>Run this device</summary>
|
||||
public void RunDeviceBefore()
|
||||
{
|
||||
if (serialPort != null) ReadDataFromSerialPort();
|
||||
}
|
||||
|
||||
/// <summary>Run this device</summary>
|
||||
public void RunDeviceAfter()
|
||||
{
|
||||
if (serialPort != null) ReadDataFromSerialPort();
|
||||
}
|
||||
|
||||
void ReadDataFromSerialPort()
|
||||
{
|
||||
/// Append any new data to the data buffer
|
||||
int receivedBytesCount = serialPort.BytesToRead;
|
||||
if (receivedBytesCount > 0)
|
||||
{
|
||||
byte[] receivedData = new byte[receivedBytesCount];
|
||||
serialPort.Read(receivedData, 0, receivedBytesCount);
|
||||
{
|
||||
string s = Telegram.LogTelegram(string.Format("{0} received ", Name), receivedData);
|
||||
Debug.WriteLine(s);
|
||||
log.Debug(s);
|
||||
}
|
||||
foreach (var b in receivedData) data.Add(b);
|
||||
}
|
||||
|
||||
if (data.Count >= MinRcvdTlgrmLen)
|
||||
{
|
||||
for (int offset = 0; offset <= (data.Count - MinRcvdTlgrmLen); offset++)
|
||||
{
|
||||
if (DataFitFrame(data, offset, KPackETelegramFrame) &&
|
||||
(data[offset + 14] == Telegram.ClaculateTelegramChecksum(SubArray(data, offset + 2, 12))))
|
||||
{
|
||||
/// Read the telegram
|
||||
KPackE_Type type = (KPackE_Type)((data[offset + 4] >> 5) & 0x07);
|
||||
KPackE_Original original = ((data[offset + 4] & 0x10) != 0) ? KPackE_Original.Original : KPackE_Original.Repeated;
|
||||
int slotNumber = (data[offset + 4] & 0x0F);
|
||||
long reading = (((data[offset + 5] * 256 + data[offset + 6]) * 256 + data[offset + 7]) * 256 + data[offset + 8]) * 16 + ((data[offset + 9] >> 4) & 0x0F);
|
||||
int account = ((data[offset + 9] & 0x0F) * 256 + data[offset + 10]) * 256 + data[offset + 11];
|
||||
int rssi = data[offset + 12];
|
||||
|
||||
if (type == KPackE_Type.Reading)
|
||||
{
|
||||
string serialNr = account.ToString();
|
||||
foreach (var rr in RegReaders)
|
||||
{
|
||||
if (rr.SerialNr == serialNr)
|
||||
{
|
||||
double wmState = (double)reading;
|
||||
rr.WMStateReceived(wmState);
|
||||
EntryForm.WMStateReceived(serialNr, wmState);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastTelegramReceived = DateTime.Now;
|
||||
SendAcknowledgeNow();
|
||||
|
||||
/// Remove processed data from the buffer
|
||||
for (int i = 0; i < offset + KPackETelegramFrame.Length; i++) data.RemoveAt(0);
|
||||
|
||||
string s;
|
||||
if (type == KPackE_Type.Reading)
|
||||
{
|
||||
s = string.Format("{0} processed KPackE telegram: s/n = {1} reading = {2}", Name, account, reading);
|
||||
}
|
||||
else
|
||||
{
|
||||
s = string.Format("{0} processed KPackE telegram: s/n = {1} {2}", Name, account, type);
|
||||
}
|
||||
Debug.WriteLine(s);
|
||||
log.Info(s);
|
||||
|
||||
break;
|
||||
}
|
||||
else if (DataFitFrame(data, offset, HartBeatTelegram))
|
||||
{
|
||||
/// acknowledge hart beat telegram
|
||||
lastTelegramReceived = DateTime.Now;
|
||||
SendAcknowledgeNow();
|
||||
|
||||
/// Remove processed data from the buffer
|
||||
for (int i = 0; i < offset + HartBeatTelegram.Length; i++) data.RemoveAt(0);
|
||||
|
||||
string s = string.Format("{0} processed HartBeat telegram", Name);
|
||||
Debug.WriteLine(s);
|
||||
log.Debug(s);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string ss = string.Format("{0}: {1} unprocessed bytes in the data buffer", Name, data.Count);
|
||||
Debug.WriteLine(ss);
|
||||
log.Debug(ss);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares received data bytes with a telegram frame.
|
||||
/// Zero bytes in the telegram frame are ignored, nonzero bytes must fit.
|
||||
/// </summary>
|
||||
/// <param name="data">Data bytes</param>
|
||||
/// <param name="offset">Offset of the fitted frame within data</param>
|
||||
/// <param name="telegramFrame">Telegram frame</param>
|
||||
/// <returns>true when data fit the frame</returns>
|
||||
bool DataFitFrame(IList<byte> data, int offset, byte[] telegramFrame)
|
||||
{
|
||||
if ((data.Count - offset) < telegramFrame.Length)
|
||||
{
|
||||
return false; /// Not enough data
|
||||
}
|
||||
|
||||
for (int i = 0; i < telegramFrame.Length; i++)
|
||||
{
|
||||
if ((telegramFrame[i] != 0) && (telegramFrame[i] != data[i + offset]))
|
||||
{
|
||||
return false; /// Data do not fit nonzero frame bytes (zero frame bytes are ignored)
|
||||
}
|
||||
}
|
||||
|
||||
return true; /// Data fit the telegram frame
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a sub-array
|
||||
/// </summary>
|
||||
public static byte[] SubArray(IList<byte> data, int index, int length)
|
||||
{
|
||||
if (data.Count < index + length) return new byte[1] { 0 };
|
||||
|
||||
byte[] result = new byte[length];
|
||||
for (int i = 0; i < length; i++) result[i] = data[index + i];
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>Stop this device</summary>
|
||||
public void StopDevice()
|
||||
{
|
||||
if (serialPort != null)
|
||||
{
|
||||
//stopWorkerThread = true;
|
||||
//workerThread.Join(2000);
|
||||
serialPort.Close();
|
||||
serialPort = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void StopDevice2() { }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user