tbf/TBF/Rig/Modbus/ConductivityMeter/ConductivityMeter.cs

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