238 lines
7.1 KiB
C#
238 lines
7.1 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using log4net;
|
|
using Common;
|
|
using TBF.Rig.Generic;
|
|
|
|
namespace TBF.Rig.Modbus.ConductivityMeter
|
|
{
|
|
public class ConductivityMeter : ComponentBase, IDevice
|
|
{
|
|
private static readonly ILog log = LogManager.GetLogger(typeof(ConductivityMeter));
|
|
public override string ToString() { return string.Format("{0}({1})", ClassName, Cfg.ToString(-1)); }
|
|
|
|
readonly ConductivityMeterCfg cfg;
|
|
|
|
Common.Modbus modbus;
|
|
int ticketNumber;
|
|
|
|
ushort[] rawRegs;
|
|
DateTime lastUpdate;
|
|
|
|
double receivedCond;
|
|
double receivedmA;
|
|
bool msrmntAvailable;
|
|
|
|
byte[] lastTelegram;
|
|
|
|
public double MeasuredVal { get { return receivedCond; } }
|
|
public double MeasuredmA { get { return receivedmA; } }
|
|
public bool MsrmntAvailable { get { return msrmntAvailable; } }
|
|
|
|
public double MsrdValLimLo { get { return cfg.MsrdValLimLo; } }
|
|
public double MsrdValLimHi { get { return cfg.MsrdValLimHi; } }
|
|
public string MsrdUnit { get { return cfg.MsrdUnit.ToString(); } }
|
|
public string MsrdFormat { get { return cfg.MsrdFormat; } }
|
|
|
|
public ConductivityMeterDiagnostics Diagnostics { get; private set; }
|
|
|
|
public ConductivityMeter()
|
|
{
|
|
Diagnostics = new ConductivityMeterDiagnostics();
|
|
}
|
|
|
|
public ConductivityMeter(IComponentCfg cfg, IList<IComponent> components)
|
|
: base(cfg)
|
|
{
|
|
this.cfg = cfg as ConductivityMeterCfg;
|
|
ticketNumber = -1;
|
|
Diagnostics = new ConductivityMeterDiagnostics();
|
|
}
|
|
|
|
public override void Initialize()
|
|
{
|
|
int regCount = Math.Max(1, (int)cfg.RegisterCount);
|
|
rawRegs = new ushort[regCount];
|
|
|
|
if (DebugLevel == DebugMode.Normal)
|
|
{
|
|
modbus = TbfComponents.FindComponent(cfg.ParentName) as Common.Modbus;
|
|
if (modbus == null) throw new Exception("Cannot find " + Name + " parent");
|
|
|
|
modbus.ComponentNames[cfg.ModbusAddress] = Name;
|
|
|
|
if (cfg.EnablePolling)
|
|
ticketNumber = modbus.RegisterForPolling();
|
|
|
|
log.FatalFormat("{0} initialized: {1}", Name, this);
|
|
}
|
|
else
|
|
{
|
|
log.FatalFormat("{0} simulated: {1}", Name, this);
|
|
}
|
|
}
|
|
|
|
public void RunDeviceBefore()
|
|
{
|
|
if (DebugLevel != DebugMode.Normal)
|
|
{
|
|
msrmntAvailable = true;
|
|
receivedmA = 12.0;
|
|
receivedCond = ConvertMilliAmpsToConductivity(receivedmA);
|
|
return;
|
|
}
|
|
|
|
var queue = modbus.ReceivedTelegrams[cfg.ModbusAddress];
|
|
|
|
if (queue.Count > 0)
|
|
{
|
|
lastTelegram = queue.Dequeue();
|
|
ParseTelegram(lastTelegram);
|
|
}
|
|
|
|
msrmntAvailable = (DateTime.Now - lastUpdate).TotalMilliseconds <= cfg.FreshnessMs;
|
|
if (!msrmntAvailable) return;
|
|
|
|
int ch = cfg.Channel;
|
|
if (ch < 0 || ch >= rawRegs.Length)
|
|
{
|
|
msrmntAvailable = false;
|
|
return;
|
|
}
|
|
|
|
double raw = rawRegs[ch];
|
|
|
|
receivedmA = ConvertRawToMilliAmps(raw);
|
|
receivedCond = ConvertMilliAmpsToConductivity(receivedmA);
|
|
|
|
float floatValue = 0;
|
|
if (rawRegs != null && rawRegs.Length >= 4)
|
|
{
|
|
floatValue = ModbusFloat(rawRegs[2], rawRegs[3]);
|
|
}
|
|
|
|
Diagnostics.SetResponse(
|
|
lastTelegram,
|
|
rawRegs,
|
|
receivedmA,
|
|
receivedCond,
|
|
cfg.MsrdUnit.ToString(),
|
|
floatValue);
|
|
}
|
|
|
|
public void RunDeviceAfter()
|
|
{
|
|
if (DebugLevel == DebugMode.Normal && cfg.EnablePolling && modbus.IsMyTurn(ticketNumber))
|
|
{
|
|
byte func = (byte)((int)cfg.ReadFunction);
|
|
|
|
Diagnostics.SetRequest(
|
|
cfg.ModbusAddress,
|
|
func,
|
|
cfg.FirstRegister,
|
|
cfg.RegisterCount);
|
|
|
|
modbus.SendMessage(
|
|
cfg.ModbusAddress,
|
|
func,
|
|
cfg.FirstRegister,
|
|
cfg.RegisterCount,
|
|
Name);
|
|
}
|
|
}
|
|
|
|
public void StopDevice() { }
|
|
public void StopDevice2() { }
|
|
|
|
private void ParseTelegram(byte[] telegram)
|
|
{
|
|
try
|
|
{
|
|
if (telegram == null || telegram.Length < 5) return;
|
|
|
|
byte func = telegram[1];
|
|
if (func != (byte)((int)cfg.ReadFunction)) return;
|
|
|
|
int byteCount = telegram[2];
|
|
int expectedRegs = rawRegs.Length;
|
|
int expectedBytes = expectedRegs * 2;
|
|
|
|
if (byteCount < expectedBytes) return;
|
|
if (telegram.Length < 3 + expectedBytes) return;
|
|
|
|
for (int i = 0; i < expectedRegs; i++)
|
|
{
|
|
int ix = 3 + i * 2;
|
|
rawRegs[i] = (ushort)((telegram[ix] << 8) | telegram[ix + 1]);
|
|
}
|
|
|
|
lastUpdate = DateTime.Now;
|
|
|
|
float floatValue = 0;
|
|
|
|
if (rawRegs.Length >= 4)
|
|
{
|
|
floatValue = ModbusFloat(rawRegs[2], rawRegs[3]);
|
|
}
|
|
|
|
Diagnostics.SetResponse(
|
|
lastTelegram,
|
|
rawRegs,
|
|
receivedmA,
|
|
receivedCond,
|
|
cfg.MsrdUnit.ToString(),
|
|
floatValue);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Diagnostics.SetError(
|
|
ex.Message + Environment.NewLine +
|
|
"Telegram: " + BitConverter.ToString(telegram));
|
|
|
|
log.WarnFormat("{0}: Failed to parse telegram. {1}", Name, ex.Message);
|
|
Debug.WriteLine(ex);
|
|
}
|
|
}
|
|
|
|
private double ConvertRawToMilliAmps(double raw)
|
|
{
|
|
if (cfg.RawFormat == RawFormat.Milliamps_x1000)
|
|
return raw / 1000.0;
|
|
|
|
double denom = cfg.RawAt20mA - cfg.RawAt4mA;
|
|
if (Math.Abs(denom) < 1e-12) return 0;
|
|
|
|
return 4.0 + (raw - cfg.RawAt4mA) * (16.0 / denom);
|
|
}
|
|
|
|
private double ConvertMilliAmpsToConductivity(double mA)
|
|
{
|
|
return cfg.CondAt4mA + (mA - 4.0) / 16.0 * (cfg.CondAt20mA - cfg.CondAt4mA);
|
|
}
|
|
|
|
public void ShowDiagnostics()
|
|
{
|
|
ConductivityMeterDiagnosticsForm form =
|
|
new ConductivityMeterDiagnosticsForm(this);
|
|
|
|
form.Show();
|
|
}
|
|
|
|
private static float ModbusFloat(ushort hi, ushort lo)
|
|
{
|
|
byte[] bytes =
|
|
{
|
|
(byte)(hi >> 8),
|
|
(byte)hi,
|
|
(byte)(lo >> 8),
|
|
(byte)lo
|
|
};
|
|
|
|
if (BitConverter.IsLittleEndian)
|
|
Array.Reverse(bytes);
|
|
|
|
return BitConverter.ToSingle(bytes, 0);
|
|
}
|
|
}
|
|
} |