Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b2276c086c | ||
|
|
0c41c750e8 |
@@ -13,5 +13,6 @@ namespace SchematicDrawing
|
|||||||
Common.Unit MsrdUnit { get; }
|
Common.Unit MsrdUnit { get; }
|
||||||
double MsrdValLimLo { get; }
|
double MsrdValLimLo { get; }
|
||||||
double MsrdValLimHi { get; }
|
double MsrdValLimHi { get; }
|
||||||
|
bool IsOffline { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -672,7 +672,11 @@ namespace SchematicDrawing
|
|||||||
SizeF size = e.Graphics.MeasureString(strVal, measuredValueFont);
|
SizeF size = e.Graphics.MeasureString(strVal, measuredValueFont);
|
||||||
Rectangle rect = new Rectangle(itm.X + itm.MsrdX, itm.Y + itm.MsrdY, (int)Math.Round(size.Width) + 4, (int)Math.Round(size.Height) + 2);
|
Rectangle rect = new Rectangle(itm.X + itm.MsrdX, itm.Y + itm.MsrdY, (int)Math.Round(size.Width) + 4, (int)Math.Round(size.Height) + 2);
|
||||||
ItemElementRectangles.Add(new ItemElementRect(item, Element.MeasuredVal, rect));
|
ItemElementRectangles.Add(new ItemElementRect(item, Element.MeasuredVal, rect));
|
||||||
if (measuredValues[msrdValIx] >= itm.MsrdValLimLo && measuredValues[msrdValIx] <= itm.MsrdValLimHi)
|
if ((item as IDrawingItemWithMeasuredVal).IsOffline)
|
||||||
|
{
|
||||||
|
e.Graphics.FillRectangle(measuredValueErrorBackColor, rect);
|
||||||
|
}
|
||||||
|
else if (measuredValues[msrdValIx] >= itm.MsrdValLimLo && measuredValues[msrdValIx] <= itm.MsrdValLimHi)
|
||||||
{
|
{
|
||||||
e.Graphics.FillRectangle(measuredValueBackColor, rect);
|
e.Graphics.FillRectangle(measuredValueBackColor, rect);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,6 +72,9 @@ namespace TBF.Rig.Ambient.Greco
|
|||||||
[XmlIgnore]
|
[XmlIgnore]
|
||||||
public IList<GNode> GNodes { get; set; }
|
public IList<GNode> GNodes { get; set; }
|
||||||
|
|
||||||
|
[XmlIgnore]
|
||||||
|
public bool IsOffline { get; set; }
|
||||||
|
|
||||||
/// Private parameterless constructor invoked by all other (public) constructors
|
/// Private parameterless constructor invoked by all other (public) constructors
|
||||||
AmbientCfg()
|
AmbientCfg()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -66,6 +66,9 @@ namespace TBF.Rig.ControlBoard.Uni
|
|||||||
[XmlIgnore]
|
[XmlIgnore]
|
||||||
public IList<GEdge> EdgesToSet { get; set; }
|
public IList<GEdge> EdgesToSet { get; set; }
|
||||||
|
|
||||||
|
[XmlIgnore]
|
||||||
|
public bool IsOffline { get; set; }
|
||||||
|
|
||||||
/// Private parameterless constructor invoked by all other (public) constructors
|
/// Private parameterless constructor invoked by all other (public) constructors
|
||||||
UniCBCfg()
|
UniCBCfg()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
///
|
using Common;
|
||||||
|
using log4net;
|
||||||
|
using SchematicDrawing;
|
||||||
|
///
|
||||||
/// Copyright (c) 2016-2020 Sensus Metering Systems
|
/// Copyright (c) 2016-2020 Sensus Metering Systems
|
||||||
///
|
///
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Common;
|
|
||||||
using log4net;
|
|
||||||
using TBF.Boxes;
|
using TBF.Boxes;
|
||||||
using TBF.Rig.Keithley.Multimeter_2010_RS232;
|
|
||||||
using TBF.Resources;
|
using TBF.Resources;
|
||||||
|
using TBF.Rig.Keithley.Multimeter_2010_RS232;
|
||||||
|
|
||||||
namespace TBF.Rig.Keithley.TempMeter
|
namespace TBF.Rig.Keithley.TempMeter
|
||||||
{
|
{
|
||||||
public class TempMeter : ComponentBase, GenericDevices.ITempMeter, GenericDevices.IHasCalendarEvents, SchematicDrawing.IDrawingItCmpnt
|
public class TempMeter : ComponentBase, GenericDevices.ITempMeter, GenericDevices.IHasCalendarEvents, IDrawingItCmpntWithMeasuredVal
|
||||||
{
|
{
|
||||||
private static readonly ILog log = LogManager.GetLogger(typeof(TempMeter));
|
private static readonly ILog log = LogManager.GetLogger(typeof(TempMeter));
|
||||||
public override string ToString() { return string.Format("{0}({1})", ClassName, Cfg.ToString(-1)); }
|
public override string ToString() { return string.Format("{0}({1})", ClassName, Cfg.ToString(-1)); }
|
||||||
|
|
||||||
@@ -36,9 +37,17 @@ namespace TBF.Rig.Keithley.TempMeter
|
|||||||
/// Dictionaries to maintain separate operations for each FloatBox argument
|
/// Dictionaries to maintain separate operations for each FloatBox argument
|
||||||
Dictionary<DoubleBox, IOperation> operations;
|
Dictionary<DoubleBox, IOperation> operations;
|
||||||
Dictionary<DoubleBox, IOperation> operations2;
|
Dictionary<DoubleBox, IOperation> operations2;
|
||||||
|
public bool IsOffline
|
||||||
|
{
|
||||||
|
get { return tempMtrCfg.IsOffline; }
|
||||||
|
set { tempMtrCfg.IsOffline = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
int invalidStreak = 0;
|
||||||
|
const int OfflineAfterInvalid = 30;
|
||||||
|
|
||||||
|
|
||||||
public TempMeter() { }
|
public TempMeter() { }
|
||||||
|
|
||||||
public TempMeter(Generic.IComponentCfg cfg, IList<Generic.IComponent> components)
|
public TempMeter(Generic.IComponentCfg cfg, IList<Generic.IComponent> components)
|
||||||
: base(cfg)
|
: base(cfg)
|
||||||
@@ -93,39 +102,66 @@ namespace TBF.Rig.Keithley.TempMeter
|
|||||||
|
|
||||||
public double ReadTemperature()
|
public double ReadTemperature()
|
||||||
{
|
{
|
||||||
double resistance = Multimeter.ReadResistance(tempMtrCfg.Channel);
|
// In non-Normal modes do not perform offline detection here
|
||||||
|
if (Cfg.DebugLevel != DebugMode.Normal)
|
||||||
|
{
|
||||||
|
if (Cfg.DebugLevel == DebugMode.Simulate)
|
||||||
|
return 20.0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
double temperature = 0;
|
double resistance;
|
||||||
if (resistance > 0 && resistance < 2000)
|
|
||||||
{
|
|
||||||
if (tempMtrCfg.UseITS90)
|
|
||||||
{
|
|
||||||
temperature = Formulas.PlatinumResistanceTM_ITS90_R2T(resistance,
|
|
||||||
tempMtrCfg.R001C,
|
|
||||||
tempMtrCfg.a7,
|
|
||||||
tempMtrCfg.b7,
|
|
||||||
tempMtrCfg.c7);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
temperature = Formulas.PlatinumResistanceTM_ITS27_R2T(resistance,
|
|
||||||
tempMtrCfg.R0,
|
|
||||||
tempMtrCfg.A,
|
|
||||||
tempMtrCfg.B);
|
|
||||||
}
|
|
||||||
|
|
||||||
log.DebugFormat("Ch = {0}, R = {1} Ohm, T = {2} °C", Channel, resistance.ToString("F3"), temperature.ToString("F3"));
|
try
|
||||||
}
|
{
|
||||||
|
resistance = Multimeter.ReadResistance(tempMtrCfg.Channel);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
invalidStreak++;
|
||||||
|
|
||||||
return temperature;
|
if (invalidStreak >= OfflineAfterInvalid)
|
||||||
|
IsOffline = true;
|
||||||
|
|
||||||
|
log.WarnFormat("Keithley TempMeter {0} ch={1}: ReadResistance failed ({2}/{3}): {4}",
|
||||||
|
Name, Channel, invalidStreak, OfflineAfterInvalid, ex.Message);
|
||||||
|
|
||||||
|
return 0; // or keep last value, or double.NaN
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid resistance => online
|
||||||
|
invalidStreak = 0;
|
||||||
|
IsOffline = false;
|
||||||
|
|
||||||
|
double temperature;
|
||||||
|
if (tempMtrCfg.UseITS90)
|
||||||
|
{
|
||||||
|
temperature = Formulas.PlatinumResistanceTM_ITS90_R2T(resistance,
|
||||||
|
tempMtrCfg.R001C,
|
||||||
|
tempMtrCfg.a7,
|
||||||
|
tempMtrCfg.b7,
|
||||||
|
tempMtrCfg.c7);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
temperature = Formulas.PlatinumResistanceTM_ITS27_R2T(resistance,
|
||||||
|
tempMtrCfg.R0,
|
||||||
|
tempMtrCfg.A,
|
||||||
|
tempMtrCfg.B);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.DebugFormat("Ch = {0}, R = {1} Ohm, T = {2} °C",
|
||||||
|
Channel, resistance.ToString("F3"), temperature.ToString("F3"));
|
||||||
|
|
||||||
|
return temperature;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Events: TempDone, Error
|
/// Events: TempDone, Error
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="temp">Reference to a variable for the temperature in Celsius</param>
|
/// <param name="temp">Reference to a variable for the temperature in Celsius</param>
|
||||||
/// <returns>ReadTempOp instance reference casted to IOperaton</returns>
|
/// <returns>ReadTempOp instance reference casted to IOperaton</returns>
|
||||||
public IOperation ReadTempOp(ref DoubleBox temp)
|
public IOperation ReadTempOp(ref DoubleBox temp)
|
||||||
{
|
{
|
||||||
IOperation operation;
|
IOperation operation;
|
||||||
if (operations.TryGetValue(temp, out operation))
|
if (operations.TryGetValue(temp, out operation))
|
||||||
|
|||||||
@@ -62,8 +62,11 @@ namespace TBF.Rig.Keithley.TempMeter
|
|||||||
[XmlIgnore]
|
[XmlIgnore]
|
||||||
public IList<GNode> GNodes { get; set; }
|
public IList<GNode> GNodes { get; set; }
|
||||||
|
|
||||||
/// Private parameterless constructor invoked by all other (public) constructors
|
[XmlIgnore]
|
||||||
TempMeterCfg()
|
public bool IsOffline { get; set; }
|
||||||
|
|
||||||
|
/// Private parameterless constructor invoked by all other (public) constructors
|
||||||
|
TempMeterCfg()
|
||||||
{
|
{
|
||||||
GNodes = new List<GNode>();
|
GNodes = new List<GNode>();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,9 +81,12 @@ namespace TBF.Rig.MettlerToledo.Standard
|
|||||||
[XmlIgnore]
|
[XmlIgnore]
|
||||||
public double MsrdValLimHi { get { return Capacity; } set { } }
|
public double MsrdValLimHi { get { return Capacity; } set { } }
|
||||||
|
|
||||||
|
[XmlIgnore]
|
||||||
|
public bool IsOffline { get; set; }
|
||||||
|
|
||||||
/// Private parameterless constructor invoked by all other (public) constructors
|
|
||||||
BalanceCfg()
|
/// Private parameterless constructor invoked by all other (public) constructors
|
||||||
|
BalanceCfg()
|
||||||
{
|
{
|
||||||
GNodes = new List<GNode>();
|
GNodes = new List<GNode>();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,8 +68,11 @@ namespace TBF.Rig.Modbus.Ambient.Comet
|
|||||||
[XmlIgnore]
|
[XmlIgnore]
|
||||||
public IList<GNode> GNodes { get; set; }
|
public IList<GNode> GNodes { get; set; }
|
||||||
|
|
||||||
/// Private parameterless constructor invoked by all other (public) constructors
|
[XmlIgnore]
|
||||||
AmbientCfg()
|
public bool IsOffline { get; set; }
|
||||||
|
|
||||||
|
/// Private parameterless constructor invoked by all other (public) constructors
|
||||||
|
AmbientCfg()
|
||||||
{
|
{
|
||||||
GNodes = new List<GNode>();
|
GNodes = new List<GNode>();
|
||||||
Format = string.Empty;
|
Format = string.Empty;
|
||||||
|
|||||||
@@ -61,8 +61,11 @@ namespace TBF.Rig.Modbus.Meret.AdjustableScale
|
|||||||
[XmlIgnore]
|
[XmlIgnore]
|
||||||
public IList<GNode> GNodes { get; set; }
|
public IList<GNode> GNodes { get; set; }
|
||||||
|
|
||||||
/// Private parameterless constructor invoked by all other (public) constructors
|
[XmlIgnore]
|
||||||
AdjustableMeterCfg()
|
public bool IsOffline { get; set; }
|
||||||
|
|
||||||
|
/// Private parameterless constructor invoked by all other (public) constructors
|
||||||
|
AdjustableMeterCfg()
|
||||||
{
|
{
|
||||||
GNodes = new List<GNode>();
|
GNodes = new List<GNode>();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
///
|
using AppDiagnostic;
|
||||||
|
using Common;
|
||||||
|
using Config.Entities;
|
||||||
|
using log4net;
|
||||||
|
using SchematicDrawing;
|
||||||
|
using SharedComponents;
|
||||||
|
///
|
||||||
/// Copyright (c) 2016-2020 Sensus Slovensko a.s.
|
/// Copyright (c) 2016-2020 Sensus Slovensko a.s.
|
||||||
///
|
///
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using log4net;
|
|
||||||
using Common;
|
|
||||||
using Config.Entities;
|
|
||||||
using SchematicDrawing;
|
|
||||||
using TBF.Rig.Generic;
|
|
||||||
using TBF.Boxes;
|
using TBF.Boxes;
|
||||||
using TBF.Resources;
|
using TBF.Resources;
|
||||||
|
using TBF.Rig.Generic;
|
||||||
using TBF.Rig.GenericDevices;
|
using TBF.Rig.GenericDevices;
|
||||||
|
|
||||||
namespace TBF.Rig.Modbus.PressureMeter.Meret
|
namespace TBF.Rig.Modbus.PressureMeter.Meret
|
||||||
@@ -35,9 +37,17 @@ namespace TBF.Rig.Modbus.PressureMeter.Meret
|
|||||||
public string AltString { get { return string.Empty; } }
|
public string AltString { get { return string.Empty; } }
|
||||||
|
|
||||||
int ticketNumber; /// 0 .. number of devices registered for regular polling - 1
|
int ticketNumber; /// 0 .. number of devices registered for regular polling - 1
|
||||||
|
public bool IsOffline
|
||||||
|
{
|
||||||
|
get { return pressureMtrCfg.IsOffline; }
|
||||||
|
set { pressureMtrCfg.IsOffline = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
int silentCycles = 0;
|
||||||
|
const int OfflineAfterSilentCycles = 30;
|
||||||
|
|
||||||
|
|
||||||
public PressureMeter() { }
|
public PressureMeter() { }
|
||||||
|
|
||||||
public PressureMeter(Generic.IComponentCfg cfg, IList<Generic.IComponent> components)
|
public PressureMeter(Generic.IComponentCfg cfg, IList<Generic.IComponent> components)
|
||||||
: base(cfg)
|
: base(cfg)
|
||||||
@@ -59,6 +69,9 @@ namespace TBF.Rig.Modbus.PressureMeter.Meret
|
|||||||
modbus.ComponentNames[pressureMtrCfg.ModbusAddress] = Name;
|
modbus.ComponentNames[pressureMtrCfg.ModbusAddress] = Name;
|
||||||
ticketNumber = modbus.RegisterForPolling();
|
ticketNumber = modbus.RegisterForPolling();
|
||||||
log.FatalFormat("{0} initialized: {1}", Name, this);
|
log.FatalFormat("{0} initialized: {1}", Name, this);
|
||||||
|
|
||||||
|
IsOffline = true;
|
||||||
|
silentCycles = OfflineAfterSilentCycles;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -68,25 +81,85 @@ namespace TBF.Rig.Modbus.PressureMeter.Meret
|
|||||||
|
|
||||||
public void RunDeviceBefore()
|
public void RunDeviceBefore()
|
||||||
{
|
{
|
||||||
if (DebugLevel == DebugMode.Normal && modbus.ReceivedTelegrams[pressureMtrCfg.ModbusAddress].Count > 0)
|
if (DebugLevel != DebugMode.Normal)
|
||||||
{
|
return;
|
||||||
byte[] telegram = modbus.ReceivedTelegrams[pressureMtrCfg.ModbusAddress].Dequeue();
|
|
||||||
if (telegram.Length == 9 && telegram[1] == 4 && telegram[2] == 4)
|
|
||||||
{
|
|
||||||
/// Swap byte order
|
|
||||||
byte t1 = telegram[3];
|
|
||||||
byte t2 = telegram[4];
|
|
||||||
byte t3 = telegram[5];
|
|
||||||
byte t4 = telegram[6];
|
|
||||||
telegram[3] = t4;
|
|
||||||
telegram[4] = t3;
|
|
||||||
telegram[5] = t2;
|
|
||||||
telegram[6] = t1;
|
|
||||||
|
|
||||||
receivedPressure = Units.ConvertFrom(Unit.kPa, System.BitConverter.ToSingle(telegram, 3));
|
bool gotValidResponse = false;
|
||||||
|
|
||||||
|
if (DebugLevel == DebugMode.Normal && modbus.ReceivedTelegrams[pressureMtrCfg.ModbusAddress].Count > 0)
|
||||||
|
{
|
||||||
|
byte[] telegram = modbus.ReceivedTelegrams[pressureMtrCfg.ModbusAddress].Dequeue();
|
||||||
|
if (telegram.Length == 9 && telegram[1] == 4 && telegram[2] == 4)
|
||||||
|
{
|
||||||
|
/// Swap byte order
|
||||||
|
byte t1 = telegram[3];
|
||||||
|
byte t2 = telegram[4];
|
||||||
|
byte t3 = telegram[5];
|
||||||
|
byte t4 = telegram[6];
|
||||||
|
telegram[3] = t4;
|
||||||
|
telegram[4] = t3;
|
||||||
|
telegram[5] = t2;
|
||||||
|
telegram[6] = t1;
|
||||||
|
|
||||||
|
receivedPressure = Units.ConvertFrom(Unit.kPa, System.BitConverter.ToSingle(telegram, 3));
|
||||||
log.WarnFormat("Pressure: {0}={1}bar", Name, receivedPressure.ToString("F3"));
|
log.WarnFormat("Pressure: {0}={1}bar", Name, receivedPressure.ToString("F3"));
|
||||||
|
|
||||||
|
gotValidResponse = true;
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
|
{
|
||||||
|
string telHex = telegram == null ? "<null>" : BitConverter.ToString(telegram);
|
||||||
|
|
||||||
|
string reason;
|
||||||
|
if (telegram == null) reason = "null telegram";
|
||||||
|
else if (telegram.Length != 9) reason = $"unexpected length (expected 9, got {telegram.Length})";
|
||||||
|
else if (telegram.Length > 1 && telegram[1] != 4) reason = $"unexpected function (expected 0x04, got 0x{telegram[1]:X2})";
|
||||||
|
else if (telegram.Length > 2 && telegram[2] != 4) reason = $"unexpected bytecount (expected 0x04, got 0x{telegram[2]:X2})";
|
||||||
|
else reason = "unknown mismatch";
|
||||||
|
|
||||||
|
//LiveLogCache.Instance.AddLog(string.Format(
|
||||||
|
// ">> RunDeviceBefore() >> component: {0} bad received telegram: {1} data={2}",
|
||||||
|
// Name,
|
||||||
|
// reason,
|
||||||
|
// telHex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bool debugOk = (DebugLevel == DebugMode.Normal);
|
||||||
|
int addr = pressureMtrCfg.ModbusAddress;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var q = modbus.ReceivedTelegrams[addr];
|
||||||
|
int qCount = (q == null) ? -1 : q.Count;
|
||||||
|
|
||||||
|
//LiveLogCache.Instance.AddLog(string.Format(
|
||||||
|
// ">> RunDeviceBefore() >> component: {0} condition failed | DebugOk={1} | Addr={2} | QueueNull={3} | QueueCount={4} | silentCycles={5}",
|
||||||
|
// Name, debugOk, addr, (q == null), qCount, silentCycles));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
//LiveLogCache.Instance.AddLog(string.Format(
|
||||||
|
// ">> RunDeviceBefore() >> component: {0} condition failed | DebugOk={1} | Addr={2} | silentCycles={3} | EXCEPTION={4}",
|
||||||
|
// Name, debugOk, addr, silentCycles, ex.Message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gotValidResponse)
|
||||||
|
{
|
||||||
|
silentCycles = 0;
|
||||||
|
IsOffline = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
silentCycles++;
|
||||||
|
if (silentCycles >= OfflineAfterSilentCycles)
|
||||||
|
{
|
||||||
|
IsOffline = true;
|
||||||
|
//LiveLogCache.Instance.AddLog(string.Format(">> RunDeviceBefore() >> component: {0} IsOffline", Name));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RunDeviceAfter()
|
public void RunDeviceAfter()
|
||||||
|
|||||||
@@ -57,8 +57,11 @@ namespace TBF.Rig.Modbus.PressureMeter.Meret
|
|||||||
[XmlIgnore]
|
[XmlIgnore]
|
||||||
public IList<GNode> GNodes { get; set; }
|
public IList<GNode> GNodes { get; set; }
|
||||||
|
|
||||||
/// Private parameterless constructor invoked by all other (public) constructors
|
[XmlIgnore]
|
||||||
PressureMeterCfg()
|
public bool IsOffline { get; set; }
|
||||||
|
|
||||||
|
/// Private parameterless constructor invoked by all other (public) constructors
|
||||||
|
PressureMeterCfg()
|
||||||
{
|
{
|
||||||
GNodes = new List<GNode>();
|
GNodes = new List<GNode>();
|
||||||
}
|
}
|
||||||
@@ -93,7 +96,7 @@ namespace TBF.Rig.Modbus.PressureMeter.Meret
|
|||||||
"Modbus address", /// 0
|
"Modbus address", /// 0
|
||||||
"Default pressure", /// 1
|
"Default pressure", /// 1
|
||||||
"Display format", /// 2
|
"Display format", /// 2
|
||||||
"Unit of pressure", /// 3
|
"Units of pressure", /// 3
|
||||||
"Pressure limit Lo", /// 4
|
"Pressure limit Lo", /// 4
|
||||||
"Pressure limit Hi", /// 5
|
"Pressure limit Hi", /// 5
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -59,8 +59,11 @@ namespace TBF.Rig.Modbus.TempControl.Easytherm
|
|||||||
[XmlIgnore]
|
[XmlIgnore]
|
||||||
public IList<GNode> GNodes { get; set; }
|
public IList<GNode> GNodes { get; set; }
|
||||||
|
|
||||||
/// <summary> Procedure parameters </summary>
|
[XmlIgnore]
|
||||||
[XmlIgnore]
|
public bool IsOffline { get; set; }
|
||||||
|
|
||||||
|
/// <summary> Procedure parameters </summary>
|
||||||
|
[XmlIgnore]
|
||||||
public ProcParams ProcParams;
|
public ProcParams ProcParams;
|
||||||
public override IParamsProvider GetRuntimeProcParamsProvider() { return ProcParams; }
|
public override IParamsProvider GetRuntimeProcParamsProvider() { return ProcParams; }
|
||||||
public override IParamsProvider CreateProcParamsProvider() { return new ProcParams(true); }
|
public override IParamsProvider CreateProcParamsProvider() { return new ProcParams(true); }
|
||||||
|
|||||||
@@ -68,9 +68,12 @@ namespace TBF.Rig.Modbus.TempControl.Novus
|
|||||||
[XmlIgnore]
|
[XmlIgnore]
|
||||||
public IList<GNode> GNodes { get; set; }
|
public IList<GNode> GNodes { get; set; }
|
||||||
|
|
||||||
|
[XmlIgnore]
|
||||||
|
public bool IsOffline { get; set; }
|
||||||
|
|
||||||
/// <summary> Procedure parameters </summary>
|
|
||||||
[XmlIgnore]
|
/// <summary> Procedure parameters </summary>
|
||||||
|
[XmlIgnore]
|
||||||
public ProcParams ProcParams;
|
public ProcParams ProcParams;
|
||||||
public override IParamsProvider GetRuntimeProcParamsProvider() { return ProcParams; }
|
public override IParamsProvider GetRuntimeProcParamsProvider() { return ProcParams; }
|
||||||
public override IParamsProvider CreateProcParamsProvider() { return new ProcParams(true); }
|
public override IParamsProvider CreateProcParamsProvider() { return new ProcParams(true); }
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
///
|
using Common;
|
||||||
|
using Config.Entities;
|
||||||
|
using log4net;
|
||||||
|
///
|
||||||
/// Copyright (c) 2021 Sensus Slovensko a.s.
|
/// Copyright (c) 2021 Sensus Slovensko a.s.
|
||||||
///
|
///
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using log4net;
|
using System.Xml.Serialization;
|
||||||
using Common;
|
|
||||||
using Config.Entities;
|
|
||||||
using TBF.Rig.Generic;
|
|
||||||
using TBF.Boxes;
|
using TBF.Boxes;
|
||||||
using TBF.Resources;
|
using TBF.Resources;
|
||||||
|
using TBF.Rig.Generic;
|
||||||
|
|
||||||
namespace TBF.Rig.Modbus.TempMeter.Groch
|
namespace TBF.Rig.Modbus.TempMeter.Groch
|
||||||
{
|
{
|
||||||
@@ -46,6 +47,24 @@ namespace TBF.Rig.Modbus.TempMeter.Groch
|
|||||||
|
|
||||||
int ticketNumber; /// 0 .. number of devices registered for regular polling - 1
|
int ticketNumber; /// 0 .. number of devices registered for regular polling - 1
|
||||||
|
|
||||||
|
//[XmlIgnore]
|
||||||
|
public bool IsOffline
|
||||||
|
{
|
||||||
|
get { return tempMtrCfg.IsOffline; }
|
||||||
|
set { tempMtrCfg.IsOffline = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shared device/channel health tracking (updated by Channel 0)
|
||||||
|
static int grochSilentCycles = 0;
|
||||||
|
static bool grochOffline = true;
|
||||||
|
|
||||||
|
// Per-channel consecutive invalid-value counters
|
||||||
|
static int[] invalidStreak = new int[TempMeterCfg.ChannelsCount];
|
||||||
|
|
||||||
|
// Thresholds
|
||||||
|
const int GrochOfflineAfterCycles = 30; // No valid telegram N cycles => Groch offline
|
||||||
|
const int ChannelOfflineAfterInvalid = 30; // Invalid value N times => channel offline
|
||||||
|
|
||||||
|
|
||||||
public TempMeter() { }
|
public TempMeter() { }
|
||||||
|
|
||||||
@@ -57,9 +76,17 @@ namespace TBF.Rig.Modbus.TempMeter.Groch
|
|||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// Ensure shared static health tracking is initialized only once
|
||||||
|
EnsureSharedInitialized();
|
||||||
|
|
||||||
if (DebugLevel == DebugMode.Normal)
|
if (DebugLevel == DebugMode.Normal)
|
||||||
{
|
{
|
||||||
/// Only TempMeter with Channel == 0 communicates via the parent Modbus component
|
// Each instance starts from shared states (will become online automatically after valid data)
|
||||||
|
bool channelOffline = invalidStreak[Channel] >= ChannelOfflineAfterInvalid;
|
||||||
|
IsOffline = grochOffline || channelOffline;
|
||||||
|
|
||||||
|
// Only TempMeter with Channel == 0 communicates via the parent Modbus component
|
||||||
if (Channel == 0)
|
if (Channel == 0)
|
||||||
{
|
{
|
||||||
modbus = TbfComponents.FindComponent(tempMtrCfg.ParentName) as Common.Modbus;
|
modbus = TbfComponents.FindComponent(tempMtrCfg.ParentName) as Common.Modbus;
|
||||||
@@ -68,10 +95,13 @@ namespace TBF.Rig.Modbus.TempMeter.Groch
|
|||||||
modbus.ComponentNames[0] = Name;
|
modbus.ComponentNames[0] = Name;
|
||||||
ticketNumber = modbus.RegisterForPolling();
|
ticketNumber = modbus.RegisterForPolling();
|
||||||
}
|
}
|
||||||
|
|
||||||
log.FatalFormat("{0} initialized: {1}", Name, this);
|
log.FatalFormat("{0} initialized: {1}", Name, this);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// In simulation you can decide whether to keep IsOffline false
|
||||||
|
IsOffline = false;
|
||||||
log.FatalFormat("{0} simulated: {1}", Name, this);
|
log.FatalFormat("{0} simulated: {1}", Name, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -110,10 +140,11 @@ namespace TBF.Rig.Modbus.TempMeter.Groch
|
|||||||
|
|
||||||
public void RunDeviceBefore()
|
public void RunDeviceBefore()
|
||||||
{
|
{
|
||||||
if (DebugLevel == DebugMode.Normal && Channel == 0)
|
/*if (DebugLevel == DebugMode.Normal && Channel == 0)
|
||||||
{
|
{
|
||||||
/// Only TempMeter with Channel == 0 communicates via the parent Modbus component
|
/// Only TempMeter with Channel == 0 communicates via the parent Modbus component
|
||||||
///
|
///
|
||||||
|
|
||||||
if (modbus.ReceivedTelegrams[0].Count > 0)
|
if (modbus.ReceivedTelegrams[0].Count > 0)
|
||||||
{
|
{
|
||||||
byte[] telegram = modbus.ReceivedTelegrams[0].Dequeue();
|
byte[] telegram = modbus.ReceivedTelegrams[0].Dequeue();
|
||||||
@@ -147,7 +178,20 @@ namespace TBF.Rig.Modbus.TempMeter.Groch
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
if (DebugLevel != DebugMode.Normal)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (Channel == 0)
|
||||||
|
{
|
||||||
|
// update shared state first (process telegrams)
|
||||||
|
UpdateSharedStateFromGroch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// now every instance (including ch0) computes its offline state from fresh shared values
|
||||||
|
bool channelOffline = invalidStreak[Channel] >= ChannelOfflineAfterInvalid;
|
||||||
|
IsOffline = grochOffline || channelOffline;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RunDeviceAfter()
|
public void RunDeviceAfter()
|
||||||
@@ -182,12 +226,21 @@ namespace TBF.Rig.Modbus.TempMeter.Groch
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
// Decide whether the raw value looks valid.
|
||||||
/// Events: TempDone, Error
|
// Adjust this to match real device behavior.
|
||||||
/// </summary>
|
static bool IsValidRaw(int raw)
|
||||||
/// <param name="temp">Reference to a variable for the temperature in Celsius</param>
|
{
|
||||||
/// <returns>ReadTempOp instance reference casted to IOperaton</returns>
|
if (raw == 0) return false;
|
||||||
public IOperation ReadTempOp(ref DoubleBox temp)
|
if (raw == 0xFFFF) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Events: TempDone, Error
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="temp">Reference to a variable for the temperature in Celsius</param>
|
||||||
|
/// <returns>ReadTempOp instance reference casted to IOperaton</returns>
|
||||||
|
public IOperation ReadTempOp(ref DoubleBox temp)
|
||||||
{
|
{
|
||||||
return new ReadTempOp(this, ref temp);
|
return new ReadTempOp(this, ref temp);
|
||||||
}
|
}
|
||||||
@@ -202,5 +255,137 @@ namespace TBF.Rig.Modbus.TempMeter.Groch
|
|||||||
{
|
{
|
||||||
return new ReadTempOp(this, ref temp, tempDone);
|
return new ReadTempOp(this, ref temp, tempDone);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// Add to class TempMeter
|
||||||
|
static readonly object initLock = new object();
|
||||||
|
static bool sharedInitialized = false;
|
||||||
|
|
||||||
|
static void EnsureSharedInitialized()
|
||||||
|
{
|
||||||
|
if (sharedInitialized) return;
|
||||||
|
|
||||||
|
lock (initLock)
|
||||||
|
{
|
||||||
|
if (sharedInitialized) return;
|
||||||
|
|
||||||
|
// Start as offline until we receive the first valid telegram
|
||||||
|
grochSilentCycles = GrochOfflineAfterCycles;
|
||||||
|
grochOffline = true;
|
||||||
|
|
||||||
|
for (int i = 0; i < TempMeterCfg.ChannelsCount; i++)
|
||||||
|
{
|
||||||
|
invalidStreak[i] = ChannelOfflineAfterInvalid; // start channels as offline until first valid value
|
||||||
|
tempRaw[i] = 0; // optional: reset raw buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
sharedInitialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateSharedStateFromGroch()
|
||||||
|
{
|
||||||
|
bool gotValidTelegram = false;
|
||||||
|
|
||||||
|
// Process all queued telegrams to avoid backlog accumulation
|
||||||
|
while (modbus.ReceivedTelegrams[0].Count > 0)
|
||||||
|
{
|
||||||
|
byte[] telegram = modbus.ReceivedTelegrams[0].Dequeue();
|
||||||
|
|
||||||
|
if (telegram.Length != 28)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
byte checksum = 0;
|
||||||
|
for (int i = 0; i < 27; i++)
|
||||||
|
checksum += telegram[i];
|
||||||
|
|
||||||
|
bool isValidGroch =
|
||||||
|
telegram[0] == '\r' &&
|
||||||
|
telegram[1] == '\n' &&
|
||||||
|
telegram[2] == GrochAddress &&
|
||||||
|
telegram[3] == 28 &&
|
||||||
|
telegram[5] == 2 &&
|
||||||
|
telegram[26] == 3 &&
|
||||||
|
telegram[27] == checksum;
|
||||||
|
|
||||||
|
if (!isValidGroch)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// At least one valid telegram received this cycle
|
||||||
|
gotValidTelegram = true;
|
||||||
|
|
||||||
|
// Extract raw channel values
|
||||||
|
if (TempMeterCfg.ChannelsCount > 0)
|
||||||
|
{
|
||||||
|
int raw = telegram[10] + 256 * telegram[11];
|
||||||
|
tempRaw[0] = raw;
|
||||||
|
invalidStreak[0] = IsValidRaw(raw) ? 0 : invalidStreak[0] + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TempMeterCfg.ChannelsCount > 1)
|
||||||
|
{
|
||||||
|
int raw = telegram[12] + 256 * telegram[13];
|
||||||
|
tempRaw[1] = raw;
|
||||||
|
invalidStreak[1] = IsValidRaw(raw) ? 0 : invalidStreak[1] + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TempMeterCfg.ChannelsCount > 2)
|
||||||
|
{
|
||||||
|
int raw = telegram[14] + 256 * telegram[15];
|
||||||
|
tempRaw[2] = raw;
|
||||||
|
invalidStreak[2] = IsValidRaw(raw) ? 0 : invalidStreak[2] + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TempMeterCfg.ChannelsCount > 3)
|
||||||
|
{
|
||||||
|
int raw = telegram[16] + 256 * telegram[17];
|
||||||
|
tempRaw[3] = raw;
|
||||||
|
invalidStreak[3] = IsValidRaw(raw) ? 0 : invalidStreak[3] + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TempMeterCfg.ChannelsCount > 4)
|
||||||
|
{
|
||||||
|
int raw = telegram[18] + 256 * telegram[19];
|
||||||
|
tempRaw[4] = raw;
|
||||||
|
invalidStreak[4] = IsValidRaw(raw) ? 0 : invalidStreak[4] + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TempMeterCfg.ChannelsCount > 5)
|
||||||
|
{
|
||||||
|
int raw = telegram[20] + 256 * telegram[21];
|
||||||
|
tempRaw[5] = raw;
|
||||||
|
invalidStreak[5] = IsValidRaw(raw) ? 0 : invalidStreak[5] + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TempMeterCfg.ChannelsCount > 6)
|
||||||
|
{
|
||||||
|
int raw = telegram[22] + 256 * telegram[23];
|
||||||
|
tempRaw[6] = raw;
|
||||||
|
invalidStreak[6] = IsValidRaw(raw) ? 0 : invalidStreak[6] + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TempMeterCfg.ChannelsCount > 7)
|
||||||
|
{
|
||||||
|
int raw = telegram[24] + 256 * telegram[25];
|
||||||
|
tempRaw[7] = raw;
|
||||||
|
invalidStreak[7] = IsValidRaw(raw) ? 0 : invalidStreak[7] + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Device-level offline detection
|
||||||
|
if (gotValidTelegram)
|
||||||
|
{
|
||||||
|
grochSilentCycles = 0;
|
||||||
|
grochOffline = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
grochSilentCycles++;
|
||||||
|
|
||||||
|
if (grochSilentCycles >= GrochOfflineAfterCycles)
|
||||||
|
{
|
||||||
|
grochOffline = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ namespace TBF.Rig.Modbus.TempMeter.Groch
|
|||||||
{
|
{
|
||||||
public class TempMeterCfg : ComponentCfgBase, IChildComponentCfg, GenericDevices.ICalibInfoCfg, IParamsProvider,
|
public class TempMeterCfg : ComponentCfgBase, IChildComponentCfg, GenericDevices.ICalibInfoCfg, IParamsProvider,
|
||||||
IDrawingItemWithMeasuredVal
|
IDrawingItemWithMeasuredVal
|
||||||
{
|
{
|
||||||
public static XmlSerializer Serializer = XmlSerializer.FromTypes(new[] { typeof(TempMeterCfg) })[0];
|
public static XmlSerializer Serializer = XmlSerializer.FromTypes(new[] { typeof(TempMeterCfg) })[0];
|
||||||
public override XmlSerializer GetSerializer() { return Serializer; }
|
public override XmlSerializer GetSerializer() { return Serializer; }
|
||||||
|
|
||||||
public IComponentCfgCtrl GetControl(IList<Config.Entities.Component> cmpntEntities)
|
public IComponentCfgCtrl GetControl(IList<Config.Entities.Component> cmpntEntities)
|
||||||
{
|
{
|
||||||
@@ -26,9 +26,9 @@ namespace TBF.Rig.Modbus.TempMeter.Groch
|
|||||||
|
|
||||||
public const int ChannelsCount = 8;
|
public const int ChannelsCount = 8;
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Serialized parameters
|
/// Serialized parameters
|
||||||
///
|
///
|
||||||
public int Address; /// 0 0 .. 255
|
public int Address; /// 0 0 .. 255
|
||||||
public int Channel; /// 1 0 .. 7 (7 = TempMeterCfg.ChannelsCount-1)
|
public int Channel; /// 1 0 .. 7 (7 = TempMeterCfg.ChannelsCount-1)
|
||||||
public double A1; /// 2
|
public double A1; /// 2
|
||||||
@@ -65,8 +65,11 @@ namespace TBF.Rig.Modbus.TempMeter.Groch
|
|||||||
[XmlIgnore]
|
[XmlIgnore]
|
||||||
public IList<GNode> GNodes { get; set; }
|
public IList<GNode> GNodes { get; set; }
|
||||||
|
|
||||||
/// Private parameterless constructor invoked by all other (public) constructors
|
[XmlIgnore]
|
||||||
TempMeterCfg()
|
public bool IsOffline { get; set; }
|
||||||
|
|
||||||
|
/// Private parameterless constructor invoked by all other (public) constructors
|
||||||
|
TempMeterCfg()
|
||||||
{
|
{
|
||||||
GNodes = new List<GNode>();
|
GNodes = new List<GNode>();
|
||||||
}
|
}
|
||||||
@@ -113,7 +116,7 @@ namespace TBF.Rig.Modbus.TempMeter.Groch
|
|||||||
"A5", /// 6
|
"A5", /// 6
|
||||||
"Default temperature", /// 7
|
"Default temperature", /// 7
|
||||||
"Display format", /// 8
|
"Display format", /// 8
|
||||||
"Unit of temperature", /// 9
|
"Units of temperature", /// 9
|
||||||
"Temperature limit Lo", /// 10
|
"Temperature limit Lo", /// 10
|
||||||
"Temperature limit Hi", /// 11
|
"Temperature limit Hi", /// 11
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -33,9 +33,17 @@ namespace TBF.Rig.Modbus.TempMeter.Meret
|
|||||||
public string AltString { get { return string.Empty; } }
|
public string AltString { get { return string.Empty; } }
|
||||||
|
|
||||||
int ticketNumber; /// 0 .. number of devices registered for regular polling - 1
|
int ticketNumber; /// 0 .. number of devices registered for regular polling - 1
|
||||||
|
public bool IsOffline
|
||||||
|
{
|
||||||
|
get { return tempMtrCfg.IsOffline; }
|
||||||
|
set { tempMtrCfg.IsOffline = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
int silentCycles = 0;
|
||||||
|
const int OfflineAfterSilentCycles = 30; // e.g. 5 cycles without valid response
|
||||||
|
|
||||||
|
|
||||||
public TempMeter() { }
|
public TempMeter() { }
|
||||||
|
|
||||||
public TempMeter(Generic.IComponentCfg cfg, IList<Generic.IComponent> components)
|
public TempMeter(Generic.IComponentCfg cfg, IList<Generic.IComponent> components)
|
||||||
: base(cfg)
|
: base(cfg)
|
||||||
@@ -50,6 +58,11 @@ namespace TBF.Rig.Modbus.TempMeter.Meret
|
|||||||
|
|
||||||
modbus.ComponentNames[tempMtrCfg.ModbusAddress] = Name;
|
modbus.ComponentNames[tempMtrCfg.ModbusAddress] = Name;
|
||||||
ticketNumber = modbus.RegisterForPolling();
|
ticketNumber = modbus.RegisterForPolling();
|
||||||
|
|
||||||
|
// Start offline until first valid response arrives (optional but consistent)
|
||||||
|
IsOffline = true;
|
||||||
|
silentCycles = OfflineAfterSilentCycles;
|
||||||
|
|
||||||
log.FatalFormat("{0} initialized: {1}", Name, this);
|
log.FatalFormat("{0} initialized: {1}", Name, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,25 +72,45 @@ namespace TBF.Rig.Modbus.TempMeter.Meret
|
|||||||
/// <summary>Run this device</summary>
|
/// <summary>Run this device</summary>
|
||||||
public void RunDeviceBefore()
|
public void RunDeviceBefore()
|
||||||
{
|
{
|
||||||
if (modbus.ReceivedTelegrams[tempMtrCfg.ModbusAddress].Count > 0)
|
if (Cfg.DebugLevel != DebugMode.Normal)
|
||||||
{
|
return;
|
||||||
byte[] telegram = modbus.ReceivedTelegrams[tempMtrCfg.ModbusAddress].Dequeue();
|
|
||||||
if (telegram.Length == 9 && telegram[1] == 4 && telegram[2] == 4)
|
|
||||||
{
|
|
||||||
/// Swap byte order
|
|
||||||
byte t1 = telegram[3];
|
|
||||||
byte t2 = telegram[4];
|
|
||||||
byte t3 = telegram[5];
|
|
||||||
byte t4 = telegram[6];
|
|
||||||
telegram[3] = t4;
|
|
||||||
telegram[4] = t3;
|
|
||||||
telegram[5] = t2;
|
|
||||||
telegram[6] = t1;
|
|
||||||
|
|
||||||
receivedTemp = System.BitConverter.ToSingle(telegram, 3);
|
bool gotValidResponse = false;
|
||||||
log.WarnFormat("Temperature: {0}={1}°C", Name, receivedTemp.ToString("F1"));
|
|
||||||
}
|
while (modbus.ReceivedTelegrams[tempMtrCfg.ModbusAddress].Count > 0)
|
||||||
}
|
{
|
||||||
|
byte[] telegram = modbus.ReceivedTelegrams[tempMtrCfg.ModbusAddress].Dequeue();
|
||||||
|
|
||||||
|
// Presence = valid response frame
|
||||||
|
if (telegram.Length == 9 && telegram[1] == 4 && telegram[2] == 4)
|
||||||
|
{
|
||||||
|
gotValidResponse = true;
|
||||||
|
|
||||||
|
// Optional: still parse temperature for display/use (does not affect presence)
|
||||||
|
byte t1 = telegram[3];
|
||||||
|
byte t2 = telegram[4];
|
||||||
|
byte t3 = telegram[5];
|
||||||
|
byte t4 = telegram[6];
|
||||||
|
telegram[3] = t4;
|
||||||
|
telegram[4] = t3;
|
||||||
|
telegram[5] = t2;
|
||||||
|
telegram[6] = t1;
|
||||||
|
|
||||||
|
receivedTemp = BitConverter.ToSingle(telegram, 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gotValidResponse)
|
||||||
|
{
|
||||||
|
silentCycles = 0;
|
||||||
|
IsOffline = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
silentCycles++;
|
||||||
|
if (silentCycles >= OfflineAfterSilentCycles)
|
||||||
|
IsOffline = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RunDeviceAfter()
|
public void RunDeviceAfter()
|
||||||
@@ -116,6 +149,17 @@ namespace TBF.Rig.Modbus.TempMeter.Meret
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool IsValidTemperature(float t)
|
||||||
|
{
|
||||||
|
// Reject NaN/Infinity
|
||||||
|
if (float.IsNaN(t) || float.IsInfinity(t)) return false;
|
||||||
|
|
||||||
|
// Put a reasonable physical range here (adjust to your real use case)
|
||||||
|
if (t < -50.0f || t > 200.0f) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Events: PressureDone, Error
|
/// Events: PressureDone, Error
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -58,8 +58,11 @@ namespace TBF.Rig.Modbus.TempMeter.Meret
|
|||||||
[XmlIgnore]
|
[XmlIgnore]
|
||||||
public IList<GNode> GNodes { get; set; }
|
public IList<GNode> GNodes { get; set; }
|
||||||
|
|
||||||
/// Private parameterless constructor invoked by all other (public) constructors
|
[XmlIgnore]
|
||||||
TempMeterCfg()
|
public bool IsOffline { get; set; }
|
||||||
|
|
||||||
|
/// Private parameterless constructor invoked by all other (public) constructors
|
||||||
|
TempMeterCfg()
|
||||||
{
|
{
|
||||||
GNodes = new List<GNode>();
|
GNodes = new List<GNode>();
|
||||||
}
|
}
|
||||||
@@ -94,7 +97,7 @@ namespace TBF.Rig.Modbus.TempMeter.Meret
|
|||||||
"Modbus address", /// 0
|
"Modbus address", /// 0
|
||||||
"Default temperature", /// 1
|
"Default temperature", /// 1
|
||||||
"Display format", /// 2
|
"Display format", /// 2
|
||||||
"Unit of temperature", /// 3
|
"Units of temperature", /// 3
|
||||||
"Temperature limit Lo", /// 4
|
"Temperature limit Lo", /// 4
|
||||||
"Temperature limit Hi", /// 5
|
"Temperature limit Hi", /// 5
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -49,8 +49,11 @@ namespace TBF.Rig.Modbus.UltrasoundLevelMeter
|
|||||||
[XmlIgnore]
|
[XmlIgnore]
|
||||||
public IList<GNode> GNodes { get; set; }
|
public IList<GNode> GNodes { get; set; }
|
||||||
|
|
||||||
/// Private parameterless constructor invoked by all other (public) constructors
|
[XmlIgnore]
|
||||||
LevelMeterCfg()
|
public bool IsOffline { get; set; }
|
||||||
|
|
||||||
|
/// Private parameterless constructor invoked by all other (public) constructors
|
||||||
|
LevelMeterCfg()
|
||||||
{
|
{
|
||||||
GNodes = new List<GNode>();
|
GNodes = new List<GNode>();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,9 +91,12 @@ namespace TBF.Rig.Scales.MettlerToledo
|
|||||||
[XmlIgnore]
|
[XmlIgnore]
|
||||||
public double MsrdValLimHi { get { return Capacity; } set { } }
|
public double MsrdValLimHi { get { return Capacity; } set { } }
|
||||||
|
|
||||||
|
[XmlIgnore]
|
||||||
|
public bool IsOffline { get; set; }
|
||||||
|
|
||||||
/// Private parameterless constructor invoked by all other (public) constructors
|
|
||||||
ScaleCfg()
|
/// Private parameterless constructor invoked by all other (public) constructors
|
||||||
|
ScaleCfg()
|
||||||
{
|
{
|
||||||
GNodes = new List<GNode>();
|
GNodes = new List<GNode>();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,6 +117,9 @@ namespace TBF.Rig.Uni.FlowMeter
|
|||||||
double msrdValLimLo = 0; /// in [m3/h]
|
double msrdValLimLo = 0; /// in [m3/h]
|
||||||
double msrdValLimHi; /// in [m3/h]
|
double msrdValLimHi; /// in [m3/h]
|
||||||
|
|
||||||
|
[XmlIgnore]
|
||||||
|
public bool IsOffline { get; set; }
|
||||||
|
|
||||||
|
|
||||||
public bool RangeEnabled(int rangeIx1)
|
public bool RangeEnabled(int rangeIx1)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -58,8 +58,11 @@ namespace TBF.Rig.Uni.FlowMetersInParallel
|
|||||||
|
|
||||||
private IEnumerable<Config.Entities.Component> flowMeters;
|
private IEnumerable<Config.Entities.Component> flowMeters;
|
||||||
|
|
||||||
/// Private parameterless constructor invoked by all other (public) constructors
|
[XmlIgnore]
|
||||||
FlowMeterCfg()
|
public bool IsOffline { get; set; }
|
||||||
|
|
||||||
|
/// Private parameterless constructor invoked by all other (public) constructors
|
||||||
|
FlowMeterCfg()
|
||||||
{
|
{
|
||||||
GNodes = new List<GNode>();
|
GNodes = new List<GNode>();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user