289 lines
9.9 KiB
C#
289 lines
9.9 KiB
C#
///
|
|
/// 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.Warn(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() { }
|
|
}
|
|
}
|