diff --git a/TBF/BenchControl/GenericDevices/IHart.cs b/TBF/BenchControl/GenericDevices/IHart.cs
new file mode 100644
index 000000000..e6aecd7c7
--- /dev/null
+++ b/TBF/BenchControl/GenericDevices/IHart.cs
@@ -0,0 +1,31 @@
+///
+/// Copyright (c) 2018 Sensus Slovensko a.s.
+///
+using System.Collections.Generic;
+using TBF.BenchControl.Generic;
+
+namespace TBF.BenchControl.GenericDevices
+{
+ public interface IHart : IComponent
+ {
+ ///
+ /// 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
+ void SendMessage(byte[] message);
+
+ ///
+ /// 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)
+ void SendMessage(byte modbusAddress, byte function, ushort dataAddress, ushort dataCount);
+
+ Queue[] ReceivedTelegrams { get; }
+ }
+}
diff --git a/TBF/BenchControl/Hart/Common/Factory.cs b/TBF/BenchControl/Hart/Common/Factory.cs
new file mode 100644
index 000000000..662873e26
--- /dev/null
+++ b/TBF/BenchControl/Hart/Common/Factory.cs
@@ -0,0 +1,26 @@
+///
+/// Copyright (c) 2018 Sensus Slovensko a.s.
+///
+using System.Collections.Generic;
+using TBF.BenchControl.Generic;
+
+namespace TBF.BenchControl.Hart.Common
+{
+ public class Factory : IComponentFactory
+ {
+ public string ClassName { get { return this.GetType().Namespace.Substring(17); } }
+
+ public void ResetStaticProperties() { Hart.ResetStaticProperties(); }
+
+ public IComponent DummyComponent() { return new Hart(); }
+
+ public IComponent GetComponent(IComponentCfg cfg, IList components) { return new Hart(cfg); }
+
+ public IComponentCfg DefaultConfig() { return new HartCfg(this.GetType().Namespace.Substring(17), this); }
+
+ public IComponentCfg CmpntCfgFromCmpntEntity(Config.Entities.Component component)
+ {
+ return ComponentCfgBase.CreateFromDbEntity(HartCfg.Serializer, component, this);
+ }
+ }
+}
diff --git a/TBF/BenchControl/Hart/Common/Hart.cs b/TBF/BenchControl/Hart/Common/Hart.cs
new file mode 100644
index 000000000..fb98022dd
--- /dev/null
+++ b/TBF/BenchControl/Hart/Common/Hart.cs
@@ -0,0 +1,211 @@
+///
+/// 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;
+
+namespace TBF.BenchControl.Hart.Common
+{
+ ///
+ /// Root component for Modbus communication via serial port (RS485)
+ ///
+ public class Hart : ComponentBase, IDevice, GenericDevices.IModbus
+ {
+ 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 fields
+ SerialPort serialPort;
+ DateTime lastSerialPortWrite;
+
+ public Queue[] ReceivedTelegrams
+ {
+ get { return receivedTelegrams; }
+ }
+ Queue[] receivedTelegrams;
+
+ Queue telegramsToSend;
+
+ public Hart()
+ {
+ }
+
+ ///
+ /// 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 Hart(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 HartCfg;
+ log.Debug(this.ToString());
+ }
+
+ ~Hart()
+ {
+ }
+
+ 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);
+
+ if ((DateTime.Now - lastSerialPortWrite) > new TimeSpan(0, 0, 0, 0, 100))
+ {
+ /// More then 100 ms since last 'send' --> do not enqueue the message
+ SendMessageNow(message);
+ }
+ else
+ {
+ 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);
+ }
+
+ 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);
+ }
+
+
+ /// 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 && (DateTime.Now - lastSerialPortWrite) > new TimeSpan(0, 0, 0, 0, 500))
+ {
+ SendMessageNow(telegramsToSend.Dequeue());
+ }
+ }
+
+ /// Stop this device
+ public void StopDevice()
+ {
+ if (serialPort != null)
+ {
+ //stopWorkerThread = true;
+ //workerThread.Join(2000);
+ serialPort.Close();
+ serialPort = null;
+ }
+ }
+
+ public void StopDevice2() { }
+ }
+}
diff --git a/TBF/BenchControl/Hart/Common/HartCfg.cs b/TBF/BenchControl/Hart/Common/HartCfg.cs
new file mode 100644
index 000000000..1a9b311d0
--- /dev/null
+++ b/TBF/BenchControl/Hart/Common/HartCfg.cs
@@ -0,0 +1,248 @@
+///
+/// Copyright (c) 2018 Sensus Slovensko a.s.
+///
+using System.IO;
+using System.Collections.Generic;
+using System.IO.Ports;
+using System.Xml.Serialization;
+using Config.Entities;
+using TBF.BenchControl.Generic;
+
+namespace TBF.BenchControl.Hart.Common
+{
+ public class HartCfg : ComponentCfgBase, Generic.IComponentCfg, Config.Entities.IParamsProvider
+ {
+ public static XmlSerializer Serializer = XmlSerializer.FromTypes(new[] { typeof(HartCfg) })[0];
+ public override XmlSerializer GetSerializer() { return Serializer; }
+
+ static readonly int[] AllBaudRates = new int[] { 300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200 };
+ static readonly Parity[] AllParities = new Parity[] { Parity.None, Parity.Even, Parity.Odd };
+ static readonly int[] AllDataBits = new int[] { 7, 8 };
+ static readonly StopBits[] AllStopBits = new StopBits[] { StopBits.None, StopBits.One, StopBits.OnePointFive, StopBits.Two };
+ static readonly Handshake[] AllHandshakes = new Handshake[] { Handshake.None, Handshake.XOnXOff, Handshake.RequestToSend, Handshake.RequestToSendXOnXOff };
+
+
+ public IComponentCfgCtrl GetControl()
+ {
+ return new Configs.ParamsProvider.ComponentCfgCtrl(this, null);
+ }
+
+ ///
+ /// Serialized parameters
+ ///
+ public int ComPortNr;
+ public int BaudRate;
+ public int DataBits;
+ public Parity Parity;
+ public StopBits StopBits;
+ public Handshake Handshake;
+
+ /// Private parameterless constructor invoked by all other (public) constructors
+ HartCfg()
+ {
+ }
+
+ public HartCfg(string name, IComponentFactory factory)
+ : this()
+ {
+ Name = name;
+ Factory = factory;
+ ParentName = string.Empty;
+ InitializeAll();
+ }
+
+ public string ComponentName { get { return Name; } }
+
+ public void InitializeAll()
+ {
+ ComPortNr = 3;
+ BaudRate = 9600;
+ DataBits = 8;
+ Parity = System.IO.Ports.Parity.None;
+ StopBits = System.IO.Ports.StopBits.One;
+ Handshake = System.IO.Ports.Handshake.None;
+ }
+ string[] paramNames = new string[]
+ {
+ "Com port number",
+ "Baud rate",
+ "Data bits",
+ "Parity",
+ "Stop bits",
+ "Handshake"
+ };
+ public string ParamName(int i) { return paramNames[i]; }
+ public int ParamsCount() { return paramNames.Length; }
+
+ public IList ParamValues(int i)
+ {
+ IList list = new List();
+ switch (i)
+ {
+ case 1: foreach (var v in AllBaudRates) list.Add(v.ToString()); break;
+ case 2: foreach (var v in AllDataBits) list.Add(v.ToString()); break;
+ case 3: foreach (var v in AllParities) list.Add(v.ToString()); break;
+ case 4: foreach (var v in AllStopBits) list.Add(v.ToString()); break;
+ case 5: foreach (var v in AllHandshakes) list.Add(v.ToString()); break;
+ default: return null;
+ }
+ return list;
+ }
+
+ public string ToString(int i)
+ {
+ if (i == -1)
+ {
+ return string.Format("Com{0}, {1}Bd, {2}-bits, parity={3}, stopBits={4}, {5}",
+ ComPortNr, BaudRate, DataBits, Parity, StopBits, Handshake);
+ }
+
+ switch (i)
+ {
+ case 0: return ComPortNr.ToString();
+ case 1: return BaudRate.ToString();
+ case 2: return DataBits.ToString();
+ case 3: return Parity.ToString();
+ case 4: return StopBits.ToString();
+ case 5: return Handshake.ToString();
+ default: return string.Empty;
+ }
+ }
+
+
+ public CfgUpdateFlags UpdateParam(int i, string strValue)
+ {
+ switch (i)
+ {
+ case 0:
+ if (ComPortNr != int.Parse(strValue))
+ {
+ ComPortNr = int.Parse(strValue);
+ return CfgUpdateFlags.RestartRqrd;
+ }
+ else
+ {
+ return CfgUpdateFlags.None;
+ }
+
+ case 1:
+ foreach (var v in AllBaudRates)
+ {
+ if ((strValue == v.ToString()) && (BaudRate != v))
+ {
+ BaudRate = v;
+ return CfgUpdateFlags.RestartRqrd;
+ }
+ }
+ return CfgUpdateFlags.None;
+
+ case 2:
+ foreach (var v in AllDataBits)
+ {
+ if ((strValue == v.ToString()) && (DataBits != v))
+ {
+ DataBits = v;
+ return CfgUpdateFlags.RestartRqrd;
+ }
+ }
+ return CfgUpdateFlags.None;
+
+ case 3:
+ foreach (var v in AllParities)
+ {
+ if ((strValue == v.ToString()) && (Parity != v))
+ {
+ Parity = v;
+ return CfgUpdateFlags.RestartRqrd;
+ }
+ }
+ return CfgUpdateFlags.None;
+
+ case 4:
+ foreach (var v in AllStopBits)
+ {
+ if ((strValue == v.ToString()) && (StopBits != v))
+ {
+ StopBits = v;
+ return CfgUpdateFlags.RestartRqrd;
+ }
+ }
+ return CfgUpdateFlags.None;
+
+ case 5:
+ foreach (var v in AllHandshakes)
+ {
+ if ((strValue == v.ToString()) && (Handshake != v))
+ {
+ Handshake = v;
+ return CfgUpdateFlags.RestartRqrd;
+ }
+ }
+ return CfgUpdateFlags.None;
+
+ default:
+ return CfgUpdateFlags.None;
+ }
+ }
+
+
+ public bool ValidateParam(int i, string strValue, out string message)
+ {
+ message = string.Empty;
+
+ switch (i)
+ {
+ case 0:
+ int comPortNr;
+ if (int.TryParse(strValue, out comPortNr) && comPortNr > 0 && comPortNr <= 999) return true;
+ break;
+ case 1:
+ foreach (var v in AllBaudRates) if (strValue == v.ToString()) return true;
+ break;
+ case 2:
+ foreach (var v in AllDataBits) if (strValue == v.ToString()) return true;
+ break;
+ case 3:
+ foreach (var v in AllParities) if (strValue == v.ToString()) return true;
+ break;
+ case 4:
+ foreach (var v in AllStopBits) if (strValue == v.ToString()) return true;
+ break;
+ case 5:
+ foreach (var v in AllHandshakes) if (strValue == v.ToString()) return true;
+ break;
+
+ default:
+ message = "Invalid index";
+ return false;
+ }
+
+ message = ParamName(i) + " is invalid";
+ return false;
+ }
+
+ void CopyContentTo(HartCfg prms)
+ {
+ prms.ParentName = this.ParentName;
+ prms.ComPortNr = this.ComPortNr;
+ prms.BaudRate = this.BaudRate;
+ prms.DataBits = this.DataBits;
+ prms.Parity = this.Parity;
+ prms.StopBits = this.StopBits;
+ prms.Handshake = this.Handshake;
+ }
+
+
+ public Config.Entities.IParamsProvider Clone()
+ {
+ HartCfg pars = new HartCfg();
+ CopyContentTo(pars);
+ return pars;
+ }
+
+ public void UpdateEmbeddedDbEntity()
+ {
+ /// Do nothing
+ }
+ }
+}
diff --git a/TBF/BenchControl/Hart/Nivotrack/Factory.cs b/TBF/BenchControl/Hart/Nivotrack/Factory.cs
new file mode 100644
index 000000000..8f7d4e1c4
--- /dev/null
+++ b/TBF/BenchControl/Hart/Nivotrack/Factory.cs
@@ -0,0 +1,26 @@
+///
+/// Copyright (c) 2018 Sensus Slovensko a.s.
+///
+using System.Collections.Generic;
+using TBF.BenchControl.Generic;
+
+namespace TBF.BenchControl.Hart.Nivotrack
+{
+ public class Factory : IComponentFactory
+ {
+ public string ClassName { get { return this.GetType().Namespace.Substring(17); } }
+
+ public void ResetStaticProperties() { Nivotrack.ResetStaticProperties(); }
+
+ public IComponent DummyComponent() { return new Nivotrack(); }
+
+ public IComponent GetComponent(IComponentCfg cfg, IList components) { return new Nivotrack(cfg); }
+
+ public IComponentCfg DefaultConfig() { return new NivotrackCfg(this.GetType().Namespace.Substring(17), this); }
+
+ public IComponentCfg CmpntCfgFromCmpntEntity(Config.Entities.Component component)
+ {
+ return ComponentCfgBase.CreateFromDbEntity(NivotrackCfg.Serializer, component, this);
+ }
+ }
+}
diff --git a/TBF/BenchControl/Hart/Nivotrack/Nivotrack.cs b/TBF/BenchControl/Hart/Nivotrack/Nivotrack.cs
new file mode 100644
index 000000000..776228068
--- /dev/null
+++ b/TBF/BenchControl/Hart/Nivotrack/Nivotrack.cs
@@ -0,0 +1,211 @@
+///
+/// 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;
+
+namespace TBF.BenchControl.Hart.Nivotrack
+{
+ ///
+ /// Root component for Modbus communication via serial port (RS485)
+ ///
+ public class Nivotrack : ComponentBase, IDevice, GenericDevices.IHart
+ {
+ private static readonly ILog log = LogManager.GetLogger(typeof(Nivotrack));
+ public override string ToString() { return string.Format("Modbus({0})", Cfg.ToString(1)); }
+
+ private readonly NivotrackCfg modbusCommonCfg;
+
+ /// Private fields
+ SerialPort serialPort;
+ DateTime lastSerialPortWrite;
+
+ public Queue[] ReceivedTelegrams
+ {
+ get { return receivedTelegrams; }
+ }
+ Queue[] receivedTelegrams;
+
+ Queue telegramsToSend;
+
+ public Nivotrack()
+ {
+ }
+
+ ///
+ /// 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 Nivotrack(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 NivotrackCfg;
+ log.Debug(this.ToString());
+ }
+
+ ~Nivotrack()
+ {
+ }
+
+ 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);
+
+ if ((DateTime.Now - lastSerialPortWrite) > new TimeSpan(0, 0, 0, 0, 100))
+ {
+ /// More then 100 ms since last 'send' --> do not enqueue the message
+ SendMessageNow(message);
+ }
+ else
+ {
+ 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);
+ }
+
+ 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);
+ }
+
+
+ /// 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 && (DateTime.Now - lastSerialPortWrite) > new TimeSpan(0, 0, 0, 0, 500))
+ {
+ SendMessageNow(telegramsToSend.Dequeue());
+ }
+ }
+
+ /// Stop this device
+ public void StopDevice()
+ {
+ if (serialPort != null)
+ {
+ //stopWorkerThread = true;
+ //workerThread.Join(2000);
+ serialPort.Close();
+ serialPort = null;
+ }
+ }
+
+ public void StopDevice2() { }
+ }
+}
diff --git a/TBF/BenchControl/Hart/Nivotrack/NivotrackCfg.cs b/TBF/BenchControl/Hart/Nivotrack/NivotrackCfg.cs
new file mode 100644
index 000000000..ac7c81209
--- /dev/null
+++ b/TBF/BenchControl/Hart/Nivotrack/NivotrackCfg.cs
@@ -0,0 +1,248 @@
+///
+/// Copyright (c) 2018 Sensus Slovensko a.s.
+///
+using System.IO;
+using System.Collections.Generic;
+using System.IO.Ports;
+using System.Xml.Serialization;
+using Config.Entities;
+using TBF.BenchControl.Generic;
+
+namespace TBF.BenchControl.Hart.Nivotrack
+{
+ public class NivotrackCfg : ComponentCfgBase, Generic.IComponentCfg, Config.Entities.IParamsProvider
+ {
+ public static XmlSerializer Serializer = XmlSerializer.FromTypes(new[] { typeof(NivotrackCfg) })[0];
+ public override XmlSerializer GetSerializer() { return Serializer; }
+
+ static readonly int[] AllBaudRates = new int[] { 300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200 };
+ static readonly Parity[] AllParities = new Parity[] { Parity.None, Parity.Even, Parity.Odd };
+ static readonly int[] AllDataBits = new int[] { 7, 8 };
+ static readonly StopBits[] AllStopBits = new StopBits[] { StopBits.None, StopBits.One, StopBits.OnePointFive, StopBits.Two };
+ static readonly Handshake[] AllHandshakes = new Handshake[] { Handshake.None, Handshake.XOnXOff, Handshake.RequestToSend, Handshake.RequestToSendXOnXOff };
+
+
+ public IComponentCfgCtrl GetControl()
+ {
+ return new Configs.ParamsProvider.ComponentCfgCtrl(this, null);
+ }
+
+ ///
+ /// Serialized parameters
+ ///
+ public int ComPortNr;
+ public int BaudRate;
+ public int DataBits;
+ public Parity Parity;
+ public StopBits StopBits;
+ public Handshake Handshake;
+
+ /// Private parameterless constructor invoked by all other (public) constructors
+ NivotrackCfg()
+ {
+ }
+
+ public NivotrackCfg(string name, IComponentFactory factory)
+ : this()
+ {
+ Name = name;
+ Factory = factory;
+ ParentName = string.Empty;
+ InitializeAll();
+ }
+
+ public string ComponentName { get { return Name; } }
+
+ public void InitializeAll()
+ {
+ ComPortNr = 3;
+ BaudRate = 9600;
+ DataBits = 8;
+ Parity = System.IO.Ports.Parity.None;
+ StopBits = System.IO.Ports.StopBits.One;
+ Handshake = System.IO.Ports.Handshake.None;
+ }
+ string[] paramNames = new string[]
+ {
+ "Com port number",
+ "Baud rate",
+ "Data bits",
+ "Parity",
+ "Stop bits",
+ "Handshake"
+ };
+ public string ParamName(int i) { return paramNames[i]; }
+ public int ParamsCount() { return paramNames.Length; }
+
+ public IList ParamValues(int i)
+ {
+ IList list = new List();
+ switch (i)
+ {
+ case 1: foreach (var v in AllBaudRates) list.Add(v.ToString()); break;
+ case 2: foreach (var v in AllDataBits) list.Add(v.ToString()); break;
+ case 3: foreach (var v in AllParities) list.Add(v.ToString()); break;
+ case 4: foreach (var v in AllStopBits) list.Add(v.ToString()); break;
+ case 5: foreach (var v in AllHandshakes) list.Add(v.ToString()); break;
+ default: return null;
+ }
+ return list;
+ }
+
+ public string ToString(int i)
+ {
+ if (i == -1)
+ {
+ return string.Format("Com{0}, {1}Bd, {2}-bits, parity={3}, stopBits={4}, {5}",
+ ComPortNr, BaudRate, DataBits, Parity, StopBits, Handshake);
+ }
+
+ switch (i)
+ {
+ case 0: return ComPortNr.ToString();
+ case 1: return BaudRate.ToString();
+ case 2: return DataBits.ToString();
+ case 3: return Parity.ToString();
+ case 4: return StopBits.ToString();
+ case 5: return Handshake.ToString();
+ default: return string.Empty;
+ }
+ }
+
+
+ public CfgUpdateFlags UpdateParam(int i, string strValue)
+ {
+ switch (i)
+ {
+ case 0:
+ if (ComPortNr != int.Parse(strValue))
+ {
+ ComPortNr = int.Parse(strValue);
+ return CfgUpdateFlags.RestartRqrd;
+ }
+ else
+ {
+ return CfgUpdateFlags.None;
+ }
+
+ case 1:
+ foreach (var v in AllBaudRates)
+ {
+ if ((strValue == v.ToString()) && (BaudRate != v))
+ {
+ BaudRate = v;
+ return CfgUpdateFlags.RestartRqrd;
+ }
+ }
+ return CfgUpdateFlags.None;
+
+ case 2:
+ foreach (var v in AllDataBits)
+ {
+ if ((strValue == v.ToString()) && (DataBits != v))
+ {
+ DataBits = v;
+ return CfgUpdateFlags.RestartRqrd;
+ }
+ }
+ return CfgUpdateFlags.None;
+
+ case 3:
+ foreach (var v in AllParities)
+ {
+ if ((strValue == v.ToString()) && (Parity != v))
+ {
+ Parity = v;
+ return CfgUpdateFlags.RestartRqrd;
+ }
+ }
+ return CfgUpdateFlags.None;
+
+ case 4:
+ foreach (var v in AllStopBits)
+ {
+ if ((strValue == v.ToString()) && (StopBits != v))
+ {
+ StopBits = v;
+ return CfgUpdateFlags.RestartRqrd;
+ }
+ }
+ return CfgUpdateFlags.None;
+
+ case 5:
+ foreach (var v in AllHandshakes)
+ {
+ if ((strValue == v.ToString()) && (Handshake != v))
+ {
+ Handshake = v;
+ return CfgUpdateFlags.RestartRqrd;
+ }
+ }
+ return CfgUpdateFlags.None;
+
+ default:
+ return CfgUpdateFlags.None;
+ }
+ }
+
+
+ public bool ValidateParam(int i, string strValue, out string message)
+ {
+ message = string.Empty;
+
+ switch (i)
+ {
+ case 0:
+ int comPortNr;
+ if (int.TryParse(strValue, out comPortNr) && comPortNr > 0 && comPortNr <= 999) return true;
+ break;
+ case 1:
+ foreach (var v in AllBaudRates) if (strValue == v.ToString()) return true;
+ break;
+ case 2:
+ foreach (var v in AllDataBits) if (strValue == v.ToString()) return true;
+ break;
+ case 3:
+ foreach (var v in AllParities) if (strValue == v.ToString()) return true;
+ break;
+ case 4:
+ foreach (var v in AllStopBits) if (strValue == v.ToString()) return true;
+ break;
+ case 5:
+ foreach (var v in AllHandshakes) if (strValue == v.ToString()) return true;
+ break;
+
+ default:
+ message = "Invalid index";
+ return false;
+ }
+
+ message = ParamName(i) + " is invalid";
+ return false;
+ }
+
+ void CopyContentTo(NivotrackCfg prms)
+ {
+ prms.ParentName = this.ParentName;
+ prms.ComPortNr = this.ComPortNr;
+ prms.BaudRate = this.BaudRate;
+ prms.DataBits = this.DataBits;
+ prms.Parity = this.Parity;
+ prms.StopBits = this.StopBits;
+ prms.Handshake = this.Handshake;
+ }
+
+
+ public Config.Entities.IParamsProvider Clone()
+ {
+ NivotrackCfg pars = new NivotrackCfg();
+ CopyContentTo(pars);
+ return pars;
+ }
+
+ public void UpdateEmbeddedDbEntity()
+ {
+ /// Do nothing
+ }
+ }
+}
diff --git a/TBF/BenchControl/Modbus/Common/HartCfg.cs b/TBF/BenchControl/Modbus/Common/HartCfg.cs
new file mode 100644
index 000000000..6491377c0
--- /dev/null
+++ b/TBF/BenchControl/Modbus/Common/HartCfg.cs
@@ -0,0 +1,54 @@
+///
+/// Copyright (c) 2015-2016 Sensus Metering Systems
+///
+using System.IO;
+using System.Collections.Generic;
+using System.IO.Ports;
+using System.Xml.Serialization;
+using TBF.BenchControl.Generic;
+
+namespace TBF.BenchControl.Modbus.Common
+{
+ public class HartCfg : ComponentCfgBase, Generic.IComponentCfg
+ {
+ public static XmlSerializer Serializer = XmlSerializer.FromTypes(new[] { typeof(HartCfg) })[0];
+ public override XmlSerializer GetSerializer() { return Serializer; }
+
+ public IComponentCfgCtrl GetControl() { return new ModbusCfgCtrl(); }
+
+ ///
+ /// Serialized parameters
+ ///
+ public int ComPortNr;
+ public int BaudRate;
+ public Parity Parity;
+ public int DataBits;
+ public StopBits StopBits;
+ public Handshake Handshake;
+
+ /// Private parameterless constructor invoked by all other (public) constructors
+ HartCfg()
+ {
+ Name = "Modbus";
+ ParentName = string.Empty;
+ ComPortNr = 3;
+ BaudRate = 9600;
+ Parity = System.IO.Ports.Parity.None;
+ DataBits = 8;
+ StopBits = System.IO.Ports.StopBits.One;
+ Handshake = System.IO.Ports.Handshake.None;
+ }
+
+ public HartCfg(IComponentFactory factory)
+ : this()
+ {
+ this.Factory = factory;
+ }
+
+ public string ToString(int i)
+ {
+ return string.Format("Name={0}, Com{1}, {2}Bd, {3}-bits, parity={4}, stopBits={5}, {6}",
+ Name, ComPortNr, BaudRate, DataBits, Parity, StopBits, Handshake);
+ }
+ }
+}
diff --git a/TBF/BenchControl/TbfComponents.cs b/TBF/BenchControl/TbfComponents.cs
index 8f56ecf64..0318732cc 100644
--- a/TBF/BenchControl/TbfComponents.cs
+++ b/TBF/BenchControl/TbfComponents.cs
@@ -83,7 +83,9 @@ namespace TBF.BenchControl
Factories.Add(new DataEntry.WMStates.EntryFormFactory());
Factories.Add(new Ambient.Comet.Factory());
Factories.Add(new Ambient.Greco.Factory());
- Factories.Add(new Keithley.Multimeter_2010_RS232.Factory()); /// Keithley.Multimeter_2010_RS232
+ Factories.Add(new Hart.Common.Factory());
+ Factories.Add(new Hart.Nivotrack.Factory());
+ Factories.Add(new Keithley.Multimeter_2010_RS232.Factory()); /// Keithley.Multimeter_2010_RS232
Factories.Add(new Keithley.TempMeter.Factory()); /// Keithley.TempMeter
Factories.Add(new MettlerToledo.Standard.BalanceFactory()); /// MettlerToledo Balance
Factories.Add(new MettlerToledo.Standard.BalanceOldFactory()); /// MettlerToledoBalanceOld
diff --git a/TBF/TBF.csproj b/TBF/TBF.csproj
index b6006e692..3fa098334 100644
--- a/TBF/TBF.csproj
+++ b/TBF/TBF.csproj
@@ -511,6 +511,7 @@
ValveCfgCtrl.cs
+
@@ -529,6 +530,12 @@
+
+
+
+
+
+