293 lines
7.4 KiB
C#
293 lines
7.4 KiB
C#
///
|
|
/// 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
|
|
{
|
|
/// <summary>
|
|
/// Root component for Modbus communication via serial port (RS485)
|
|
/// </summary>
|
|
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 <ChannelFrom, ChannelTo> is used as an index to this array
|
|
|
|
|
|
/// Private fields
|
|
SerialPort serialPort;
|
|
StringBuilder receivedData;
|
|
int readAttemptsCount;
|
|
|
|
|
|
IList<int> listOfReadOperations;
|
|
IList<int> 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_SwitchChnl,
|
|
SwitchChnl_Read,
|
|
Read_Receive,
|
|
Receive_SwitchChnl,
|
|
}
|
|
ScanState scanState;
|
|
|
|
|
|
public Multimeter()
|
|
{
|
|
Resistance = new double[ChannelTo + 1];
|
|
}
|
|
|
|
/// <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 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<int>();
|
|
listOfChannels = new List<int>();
|
|
nextChannelIx = 0;
|
|
currentChannel = 0; /// invalid, no :rout:clos command yet
|
|
scanState = ScanState.Off;
|
|
|
|
log.Debug(this.ToString());
|
|
}
|
|
|
|
~Multimeter()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Start reading temperature from specified channel
|
|
/// </summary>
|
|
/// <param name="channel">channel in range ChannelFrom .. ChannelTo</param>
|
|
/// <returns>true when successful</returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stop reading temperature from specified channel
|
|
/// </summary>
|
|
/// <param name="channel">channel in range ChannelFrom .. ChannelTo</param>
|
|
/// <returns>true when successful</returns>
|
|
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());
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Send an ASCII string, appends <CR><LF> at the end.
|
|
/// </summary>
|
|
/// <param name="message">ASCII string</param>
|
|
public void SendMessage(string message)
|
|
{
|
|
if (multimeterCfg.DebugLevel == DebugMode.Simulate) return;
|
|
serialPort.Write(message + "\r\n");
|
|
log.DebugFormat("Sending message {0}", message);
|
|
}
|
|
|
|
|
|
/// <summary>Run this device</summary>
|
|
public void RunDeviceBefore()
|
|
{
|
|
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 <CR><LF> at the end of received data and the text before <CR><LF> 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>Run this device</summary>
|
|
public void RunDeviceAfter()
|
|
{
|
|
switch (scanState)
|
|
{
|
|
case ScanState.Start_Reset:
|
|
|
|
SendMessage("*RST");
|
|
SendMessage("*CLS");
|
|
SendMessage(":conf:fres");
|
|
scanState = ScanState.Reset_SwitchChnl;
|
|
return;
|
|
|
|
case ScanState.Reset_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;
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>Stop this device</summary>
|
|
public void StopDevice()
|
|
{
|
|
serialPort.Close();
|
|
}
|
|
}
|
|
}
|