///
/// 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;
}
}
}
}