/// /// Copyright (c) 2016 Sensus Metering Systems /// using System; using System.Collections.Generic; using System.Globalization; using System.IO.Ports; using System.Text; using System.Threading; using log4net; using Config.Entities; using TBF.BenchControl.Generic; using TBF.Boxes; namespace TBF.BenchControl.Keithley.Multimeter_2010_RS232 { /// /// Root component for Modbus communication via serial port (RS485) /// public class Multimeter : ComponentBase, IDevice { private static readonly ILog log = LogManager.GetLogger(typeof(Multimeter)); public override string ToString() { return string.Format("{0}({1})", GetType().Namespace.Substring(17), Cfg.ToString(1)); } private readonly MultimeterCfg multimeterCfg; /// /// Main result /// public const int ChannelFrom = 1; public const int ChannelTo = 5; /// public double[] Resistance; /// ChannelNr in range is used as an index to this array /// Private fields SerialPort serialPort; StringBuilder receivedData; int readAttemptsCount; IList listOfReadOperations; IList listOfChannels; int nextChannelIx; /// index to listOfChannels int currentChannel; /// current channel (ChannelFrom .. ChannelTo), 0 = invalid, updated when sending :rout:clos command enum ScanState { Off, Start_Reset, Reset_Reset2, Reset2_SwitchChnl, SwitchChnl_Read, Read_Receive, Receive_SwitchChnl, } ScanState scanState; public Multimeter() { Resistance = new double[ChannelTo + 1]; } /// /// 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 Multimeter(Generic.IComponentCfg cfg) : base(cfg) { multimeterCfg = cfg as MultimeterCfg; serialPort = null; receivedData = new StringBuilder(); readAttemptsCount = 0; Resistance = new double[ChannelTo + 1]; for (int i = 0; i < Resistance.Length; i++) Resistance[i] = 0; listOfReadOperations = new List(); listOfChannels = new List(); nextChannelIx = 0; currentChannel = 0; /// invalid, no :rout:clos command yet scanState = ScanState.Off; log.Debug(this.ToString()); } ~Multimeter() { } /// /// Start reading temperature from specified channel /// /// channel in range ChannelFrom .. ChannelTo /// true when successful public bool StartReadingMultimeter(int channel) { log.DebugFormat("StartReadingMultimeter({0})", channel); if (channel < ChannelFrom || channel > ChannelTo) return false; /// Invalid channel listOfReadOperations.Add(channel); if (!listOfChannels.Contains(channel)) { listOfChannels.Add(channel); if (listOfChannels.Count == 1) StartScanning(); /// Count changed from 0 to 1 } return true; } /// /// Stop reading temperature from specified channel /// /// channel in range ChannelFrom .. ChannelTo /// true when successful public bool StopReadingMultimeter(int channel) { log.DebugFormat("StopReadingMultimeter({0})", channel); if (channel < ChannelFrom || channel > ChannelTo) return false; /// Invalid channel if (!listOfReadOperations.Remove(channel)) return false; /// Channel not found on the list (no matching StartReading...) for (int i = 0; i < listOfChannels.Count; i++) { if (listOfChannels[i] == channel) { listOfChannels.RemoveAt(i); if (listOfChannels.Count == 0) { StopScanning(); } else if (nextChannelIx > i) { nextChannelIx--; } } } return true; } public double ReadResistance(int channelNr) { if (channelNr < ChannelFrom || channelNr > ChannelTo) return 0; return Resistance[channelNr]; } private void StartScanning() { for (int i = 0; i < Resistance.Length; i++) Resistance[i] = 0; /// Reset resistances (0 == invalid) /// nextChannelIx = 0; currentChannel = 0; /// invalid, no :rout:clos command yet scanState = ScanState.Start_Reset; } private void StopScanning() { scanState = ScanState.Off; } public void Initialize() { if (multimeterCfg.DebugLevel == DebugMode.Simulate) { log.FatalFormat("Simulated device {0}", ToString()); return; } string comPortName = "COM" + multimeterCfg.ComPortNr.ToString(); serialPort = new SerialPort(comPortName, multimeterCfg.BaudRate, multimeterCfg.Parity, multimeterCfg.DataBits, multimeterCfg.StopBits); serialPort.Handshake = multimeterCfg.Handshake; serialPort.Open(); log.FatalFormat("Successfully initialized device {0}", ToString()); } /// /// Send an ASCII string, appends at the end. /// /// ASCII string public void SendMessage(string message) { if (multimeterCfg.DebugLevel == DebugMode.Simulate) return; serialPort.Write(message + "\r\n"); log.DebugFormat("Sending message {0}", message); } /// Run this device public void RunDeviceBefore() { if (serialPort == null) return; receivedData.Append(serialPort.ReadExisting()); if (scanState == ScanState.Read_Receive) { if(currentChannel >= ChannelFrom && currentChannel <= ChannelTo) { int len = receivedData.Length; if (len >= 2 && receivedData[len - 2] == '\r' && receivedData[len - 1] == '\n') { string resStr = receivedData.ToString().Substring(0, len - 2); log.DebugFormat("Received message {0}", resStr); /// Check whether there is at the end of received data and the text before can be parsed. double res; if (Utils.TryParseEDouble(resStr, out res)) { Resistance[currentChannel] = Math.Abs(res); /// Store the parsed number log.DebugFormat("Resistance[{0}] = {1}", currentChannel, res); } scanState = ScanState.Receive_SwitchChnl; } else { if (++readAttemptsCount >= 5) { scanState = ScanState.Receive_SwitchChnl; readAttemptsCount = 0; } } } else { scanState = ScanState.Receive_SwitchChnl; } } } /// Run this device public void RunDeviceAfter() { if (serialPort == null) return; switch (scanState) { case ScanState.Start_Reset: SendMessage("*RST"); scanState = ScanState.Reset_Reset2; return; case ScanState.Reset_Reset2: SendMessage(":conf:fres"); scanState = ScanState.Reset2_SwitchChnl; return; case ScanState.Reset2_SwitchChnl: case ScanState.Receive_SwitchChnl: if (nextChannelIx >= 0 && nextChannelIx < listOfChannels.Count) { /// Update 'curentChannel' currentChannel = listOfChannels[nextChannelIx]; /// Switch to the 'curentChannel' SendMessage(string.Format(":rout:clos (@{0})", currentChannel)); /// Increase 'nextChannelIx' nextChannelIx++; if (nextChannelIx >= listOfChannels.Count) nextChannelIx = 0; } scanState = ScanState.SwitchChnl_Read; return; case ScanState.SwitchChnl_Read: SendMessage(":read?"); scanState = ScanState.Read_Receive; receivedData.Clear(); readAttemptsCount = 0; return; default: /// Scanning is off return; } } /// Stop this device public void StopDevice() { if (multimeterCfg.DebugLevel == DebugMode.Simulate) return; if (serialPort != null) { serialPort.Close(); serialPort = null; } } } }