/// /// Copyright (c) 2015-2017 Sensus Metering Systems /// 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; namespace TBF.BenchControl.Modbus.Common { /// /// Root component for Modbus communication via serial port (RS485) /// public class Modbus : ComponentBase, IDevice, GenericDevices.IModbus { private static readonly ILog log = LogManager.GetLogger(typeof(Modbus)); public override string ToString() { return string.Format("Modbus({0})", Cfg.ToString(1)); } private readonly ModbusCfg modbusCommonCfg; /// Private fields SerialPort serialPort; public Queue[] ReceivedTelegrams { get { return receivedTelegrams; } } Queue[] receivedTelegrams; Queue telegramsToSend; public Modbus() { } /// /// 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. /// public Modbus(Generic.IComponentCfg cfg) : base(cfg) { receivedTelegrams = new Queue[256]; for (int i = 0; i < receivedTelegrams.Length; i++) { receivedTelegrams[i] = new Queue(); } telegramsToSend = new Queue(); serialPort = null; modbusCommonCfg = cfg as ModbusCfg; log.Debug(this.ToString()); } ~Modbus() { } public void Initialize() { if (modbusCommonCfg.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; serialPort.Open(); //stopWorkerThread = false; //workerThread = new Thread(Worker); //workerThread.Start(); log.FatalFormat("{0} - Device successfully initialized", Name); } /// /// Send an arbitrary modbus message. /// When the message length is N, however only bytes 1..N-2 have to be set. /// The last two message bytes (CRC) may be uninitialized or zero. /// They are calculated inside this function as required by Modbus specification. /// /// Message incl CRC fields, CRC bytes dont have to be set public void SendMessage(byte[] message) { if (serialPort == null) return; Telegram.UpdateTelegramCRC(message); telegramsToSend.Enqueue(message); string s = Telegram.LogTelegram(string.Format("{0} - Enqueueing message ", Name), message); Debug.WriteLine(s); log.Debug(s); } /// /// Send a structured modbus message. /// /// Device address (1..255) or 0 = broadcast /// Function (0..127) /// Address of data to be transferred (0..65535) /// Count of data bytes to be transferred (0..65535) public void SendMessage(byte modbusAddress, byte function, ushort dataAddress, ushort dataCount) { 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); } /// Run this device public void RunDeviceBefore() { if (serialPort == null) return; int nrBytes = serialPort.BytesToRead; if (nrBytes > 0) { byte[] buffer = new byte[nrBytes]; serialPort.Read(buffer, 0, nrBytes); int deviceAddress = (nrBytes >= 1) ? buffer[0] : 0; int function = (nrBytes >= 2) ? buffer[1] : 0; if ((nrBytes >= 4) && Telegram.VerifyTelegramCRC(buffer)) { receivedTelegrams[deviceAddress].Enqueue(buffer); string s = string.Format("{0} - {1} address={2} count={3}", Name, Telegram.LogTelegram("Telegram received: ", buffer), deviceAddress, receivedTelegrams[deviceAddress].Count); Debug.WriteLine(s); log.Debug(s); } else { string s = Telegram.LogTelegram(string.Format("{0} - Invalid data received ", Name), buffer); Debug.WriteLine(s); log.Debug(s); } } } /// Run this device public void RunDeviceAfter() { if (serialPort == null) return; if (telegramsToSend.Count > 0) { byte[] message = telegramsToSend.Dequeue(); serialPort.Write(message, 0, message.Length); string s = Telegram.LogTelegram(string.Format("{0} - Sending message ", Name), message); Debug.WriteLine(s); log.Debug(s); } } /// Stop this device public void StopDevice() { if (serialPort != null) { //stopWorkerThread = true; //workerThread.Join(2000); serialPort.Close(); serialPort = null; } } } }