TempControl.Novus : (1) fixes in sequence operations implementation, (2) N1040 support, ver. 2.28.1924

This commit is contained in:
Milan Hanajik 2022-06-01 13:25:56 +02:00
parent a7a7fbd403
commit e12a3eecba
4 changed files with 222 additions and 80 deletions

View File

@ -29,5 +29,5 @@ using System.Runtime.InteropServices;
// Build Number
// Revision
//
[assembly: AssemblyVersion("2.28.1921.0")]
[assembly: AssemblyFileVersion("2.28.1921.0")]
[assembly: AssemblyVersion("2.28.1924.0")]
[assembly: AssemblyFileVersion("2.28.1924.0")]

View File

@ -7,6 +7,16 @@ using TBF.Boxes;
namespace TBF.Rig.Modbus.TempControl.Novus
{
public enum SetpSource
{
TempArgument,
ProcParamA,
ProcParamB,
ProcParamC,
ProcParamD,
ProcParamE
}
public class SetTemperatureSetpointOp : IOperation
{
private static readonly ILog log = LogManager.GetLogger(typeof(SetTemperatureSetpointOp));
@ -14,6 +24,7 @@ namespace TBF.Rig.Modbus.TempControl.Novus
/// Set by the constructor
readonly TControl tempControl;
readonly SetpSource spSrc;
readonly double temperature;
readonly Event eventDone;
@ -22,24 +33,33 @@ namespace TBF.Rig.Modbus.TempControl.Novus
/// </summary>
/// <param name="tempControl">Temp. controller reference</param>
/// <param name="eventDone">Event to be returned by Run() when completed OK</param>
public SetTemperatureSetpointOp(TControl tempControl, double temperature, Event eventDone)
public SetTemperatureSetpointOp(TControl tempControl, SetpSource spSrc, double temperature, Event eventDone = Event.ConditionNotMet)
{
if (tempControl == null) throw new ArgumentNullException("tempControl");
this.tempControl = tempControl;
this.spSrc = spSrc;
this.temperature = temperature;
this.eventDone = eventDone;
log.Debug(this.ToString());
}
public SetTemperatureSetpointOp(TControl tempControl, double temperature)
: this(tempControl, temperature, Event.ConditionNotMet)
{
}
/// <summary>Start this operation</summary>
public void Start()
{
tempControl.SetTemperatureSetpoint(temperature);
double selectedTemp;
switch (spSrc)
{
default:
case SetpSource.TempArgument: selectedTemp = temperature; break;
case SetpSource.ProcParamA: selectedTemp = tempControl.ProcParamA; break;
case SetpSource.ProcParamB: selectedTemp = tempControl.ProcParamB; break;
case SetpSource.ProcParamC: selectedTemp = tempControl.ProcParamC; break;
case SetpSource.ProcParamD: selectedTemp = tempControl.ProcParamD; break;
case SetpSource.ProcParamE: selectedTemp = tempControl.ProcParamE; break;
}
tempControl.SetTemperatureSetpoint(selectedTemp);
}
/// <summary>Run this operation</summary>

View File

@ -19,14 +19,27 @@ namespace TBF.Rig.Modbus.TempControl.Novus
const UInt16 SetpointAddress = 0; /// Temperature setpoint address
const UInt16 ProcessValueAddress = 1; /// Actual temperature address
const UInt16 Status1Address = 6; /// Status word 1 address
const UInt16 FWVersionAddress = 7; /// Firmware version address
const UInt16 ControllerIdAddress = 8; /// Controller ID address
const UInt16 Status2Address = 9; /// Status word 2 address
const UInt16 Status3Address = 10; /// Status word 3 address
const UInt16 N1200_FWVerAddr = 7; /// Firmware ver. address (N1200/N1200HC)
const UInt16 N1200_CtrlIdAddr = 8; /// Controller ID address (N1200/N1200HC)
const UInt16 N1200_Status1Addr = 6; /// Status word 1 address (N1200/N1200HC)
const UInt16 N1200_Status2Addr = 9; /// Status word 2 address (N1200/N1200HC)
const int N1200_WordsCount = 10;
const UInt16 N1040_FWVerAddr = 8; /// Firmware ver. address (N1040)
const UInt16 N1040_CtrlIdAddr = 9; /// Controller ID address (N1040)
const UInt16 N1040_Status1Addr = 12; /// Status word 1 address (N1040)
const UInt16 N1040_Status2Addr = 13; /// Status word 2 address (N1040)
const int N1040_WordsCount = 14;
readonly TControlCfg novusCfg;
public double ProcParamA { get { return novusCfg.ProcParams != null ? novusCfg.ProcParams.Temp_A : 0; } }
public double ProcParamB { get { return novusCfg.ProcParams != null ? novusCfg.ProcParams.Temp_B : 0; } }
public double ProcParamC { get { return novusCfg.ProcParams != null ? novusCfg.ProcParams.Temp_C : 0; } }
public double ProcParamD { get { return novusCfg.ProcParams != null ? novusCfg.ProcParams.Temp_D : 0; } }
public double ProcParamE { get { return novusCfg.ProcParams != null ? novusCfg.ProcParams.Temp_E : 0; } }
Common.Modbus modbus;
int ticketNumber; /// 0 .. number of devices registered for regular polling - 1
@ -86,14 +99,43 @@ namespace TBF.Rig.Modbus.TempControl.Novus
Int16 status1;
Int16 status2;
Int16 status3;
Int16 fwVersion;
Int16 controllerId;
double temperatureSetpoint;
double actualTemperature;
bool isHeating { get { return (status2 & (1 << 11)) != 0; } } /// Output 1: Heating
bool isCooling { get { return (status2 & (1 << 12)) != 0; } } /// Output 2: Cooling
bool isHeating
{
get
{
switch (novusCfg.Type)
{
case Type.N1200HC:
case Type.N1200:
return (status2 & (1 << 11)) != 0; /// Output 1 set
case Type.N1040:
default:
return false; /// isHeating info unknown
}
}
}
bool isCooling
{
get
{
switch (novusCfg.Type)
{
case Type.N1200HC:
case Type.N1200:
return (status2 & (1 << 12)) != 0; /// Output 2 set
case Type.N1040:
default:
return false; /// isCooling info unknown
}
}
}
bool isFahrenheit { get { return (status2 & (1 << 9)) != 0; } }
@ -144,20 +186,22 @@ namespace TBF.Rig.Modbus.TempControl.Novus
{
log.InfoFormat("[{0}] received 5 bytes", Name);
}
else if (telegram.Length == 27 && telegram[1] == (byte)Function.ReadHoldingRegisters && telegram[2] == 22)
else if ((novusCfg.Type == Type.N1200HC || novusCfg.Type == Type.N1200) &&
telegram.Length == 2 * N1200_WordsCount + 5 &&
telegram[1] == (byte)Function.ReadHoldingRegisters &&
telegram[2] == 2 * N1200_WordsCount)
{
Int16[] values = new Int16[11];
Int16[] values = new Int16[N1200_WordsCount];
for (int i = 0; i < values.Length; i++)
{
values[i] = (Int16)(256 * telegram[2 * i + 3] + telegram[2 * i + 4]
- ((telegram[2 * i + 3] & 0x80) != 0 ? 65536 : 0));
}
status1 = values[Status1Address];
status2 = values[Status2Address];
status3 = values[Status3Address];
fwVersion = values[FWVersionAddress];
controllerId = values[ControllerIdAddress];
status1 = values[N1200_Status1Addr];
status2 = values[N1200_Status2Addr];
fwVersion = values[N1200_FWVerAddr];
controllerId = values[N1200_CtrlIdAddr];
double setpointCorF = (double)values[SetpointAddress] * 0.1;
temperatureSetpoint = isFahrenheit ? Units.ConvertFrom(Unit.F, setpointCorF) : setpointCorF;
@ -173,9 +217,44 @@ namespace TBF.Rig.Modbus.TempControl.Novus
StateMachine.ControlBoard.SetReservoirTemp(2 * (novusCfg.ReservoirNr - 1) + 1, setpointStr, Convert.ToSingle(temperatureSetpoint));
log.InfoFormat("[{0}] setpoint = {1} temperature = {2}", Name, temperatureSetpoint, actualTemperature);
log.InfoFormat("[{0}] s1=0x{1} s2=0x{2} s3=0x{3} fw={4} id={5}",
Name, status1.ToString("X4"), status2.ToString("X4"), status3.ToString("X4"),
fwVersion, controllerId == 18 ? "N1200HC" : controllerId == 48 ? "N1200" : "unknonw");
log.InfoFormat("[{0}] s1=0x{1} s2=0x{2} fw={3} id={4}",
Name, status1.ToString("X4"), status2.ToString("X4"), fwVersion,
controllerId == 18 ? "N1200HC" : controllerId == 48 ? "N1200" : "unknown");
}
else if (novusCfg.Type == Type.N1040 &&
telegram.Length == 2 * N1040_WordsCount + 5 &&
telegram[1] == (byte)Function.ReadHoldingRegisters &&
telegram[2] == 2 * N1040_WordsCount)
{
Int16[] values = new Int16[N1040_WordsCount];
for (int i = 0; i < values.Length; i++)
{
values[i] = (Int16)(256 * telegram[2 * i + 3] + telegram[2 * i + 4]
- ((telegram[2 * i + 3] & 0x80) != 0 ? 65536 : 0));
}
status1 = values[N1040_Status1Addr];
status2 = values[N1040_Status2Addr];
fwVersion = values[N1040_FWVerAddr];
controllerId = values[N1040_CtrlIdAddr];
double setpointCorF = (double)values[SetpointAddress] * 0.1;
temperatureSetpoint = isFahrenheit ? Units.ConvertFrom(Unit.F, setpointCorF) : setpointCorF;
string setpointStr = string.Format(novusCfg.SetpFormat, Units.ConvertTo(novusCfg.SetpUnit, temperatureSetpoint));
double temperatureCorF = (double)values[ProcessValueAddress] * 0.1;
actualTemperature = isFahrenheit ? Units.ConvertFrom(Unit.F, temperatureCorF) : temperatureCorF;
string temperatureStr = string.Format(novusCfg.MsrdFormat, Units.ConvertTo(novusCfg.MsrdUnit, actualTemperature));
novusDataValid = true;
StateMachine.ControlBoard.SetReservoirTemp(2 * (novusCfg.ReservoirNr - 1), temperatureStr, Convert.ToSingle(actualTemperature));
StateMachine.ControlBoard.SetReservoirTemp(2 * (novusCfg.ReservoirNr - 1) + 1, setpointStr, Convert.ToSingle(temperatureSetpoint));
log.InfoFormat("[{0}] setpoint = {1} temperature = {2}", Name, temperatureSetpoint, actualTemperature);
log.InfoFormat("[{0}] s1=0x{1} s2=0x{2} fw={3} id={4}",
Name, status1.ToString("X4"), status2.ToString("X4"), fwVersion,
controllerId == 66 ? "N1040" : "unknown");
}
else
{
@ -200,8 +279,16 @@ namespace TBF.Rig.Modbus.TempControl.Novus
if (DebugLevel != DebugMode.Simulate && DebugLevel != DebugMode.FailureDuringOperation
&& (StateMachine.Time % 3 == novusCfg.ReservoirNr - 1))
{
/// Read setpoint, PV (actual temperature), status1, status2, status3, FW version etc.
SendValueToNovus(Function.ReadHoldingRegisters, 0, 11);
if (novusCfg.Type == Type.N1200HC || novusCfg.Type == Type.N1200)
{
/// Read SP (setpoint), PV (actual temperature), status1, status2, status3, FW version etc.
SendValueToNovus(Function.ReadHoldingRegisters, 0, N1200_WordsCount);
}
else if (novusCfg.Type == Type.N1040)
{
/// Read SP (setpoint) and PV (actual temperature)
SendValueToNovus(Function.ReadHoldingRegisters, 0, N1040_WordsCount);
}
}
}
@ -263,14 +350,14 @@ namespace TBF.Rig.Modbus.TempControl.Novus
return actualTemperature;
}
/// <summary>
/// Events: SetOpututsDone, Error
/// </summary>
/// <returns>SetOutputsOp instance reference casted to IOperaton</returns>
public IOperation SetTemperatureSetpointOp(double temperature)
{
return new SetTemperatureSetpointOp(this, temperature);
}
/// <summary>
/// Events: SetOpututsDone, Error
/// </summary>
/// <returns>SetOutputsOp instance reference casted to IOperaton</returns>
public IOperation SetTemperatureSetpointOp(double temperature)
{
return new SetTemperatureSetpointOp(this, SetpSource.TempArgument, temperature);
}
public IOperation ReadTempOp(ref DoubleBox temp)
{
@ -329,11 +416,11 @@ namespace TBF.Rig.Modbus.TempControl.Novus
void CreateConditions(bool createOps)
{
ClearConditions();
AddCondition("Set temperature setpoint A", createOps, SetTemperatureSetpointOp(novusCfg.ProcParams.Temp_A));
AddCondition("Set temperature setpoint B", createOps, SetTemperatureSetpointOp(novusCfg.ProcParams.Temp_B));
AddCondition("Set temperature setpoint C", createOps, SetTemperatureSetpointOp(novusCfg.ProcParams.Temp_C));
AddCondition("Set temperature setpoint D", createOps, SetTemperatureSetpointOp(novusCfg.ProcParams.Temp_D));
AddCondition("Set temperature setpoint E", createOps, SetTemperatureSetpointOp(novusCfg.ProcParams.Temp_E));
AddCondition(string.Format("Set {0} setpoint A", Name), createOps, new SetTemperatureSetpointOp(this, SetpSource.ProcParamA, 0));
AddCondition(string.Format("Set {0} setpoint B", Name), createOps, new SetTemperatureSetpointOp(this, SetpSource.ProcParamB, 0));
AddCondition(string.Format("Set {0} setpoint C", Name), createOps, new SetTemperatureSetpointOp(this, SetpSource.ProcParamC, 0));
AddCondition(string.Format("Set {0} setpoint D", Name), createOps, new SetTemperatureSetpointOp(this, SetpSource.ProcParamD, 0));
AddCondition(string.Format("Set {0} setpoint E", Name), createOps, new SetTemperatureSetpointOp(this, SetpSource.ProcParamE, 0));
}
}
}

View File

@ -12,6 +12,14 @@ using TBF.Rig.Generic;
namespace TBF.Rig.Modbus.TempControl.Novus
{
public enum Type
{
N1200HC,
N1200,
N1040,
Count
}
public class TControlCfg : ComponentCfgBase, IChildComponentCfg, IParamsProvider
{
public static XmlSerializer Serializer = XmlSerializer.FromTypes(new[] { typeof(TControlCfg) })[0];
@ -28,15 +36,16 @@ namespace TBF.Rig.Modbus.TempControl.Novus
/// Serialized parameters
///
public int ModbusAddress; /// 0: 1..254
public string MsrdFormat { get; set; } /// 1
public Unit MsrdUnit { get; set; } /// 2
public Type Type; /// 1: N1200HC, N1200 or N1040
public string MsrdFormat { get; set; } /// 2
public Unit MsrdUnit { get; set; } /// 3
public double MsrdValLimLo { get; set; }
public double MsrdValLimHi { get; set; }
public string SetpFormat { get; set; } /// 3
public Unit SetpUnit { get; set; } /// 4
public double SetpStep { get; set; } /// 5
public bool RoundToSetpStep { get; set; } /// 6
public int ReservoirNr { get; set; } /// 7
public string SetpFormat { get; set; } /// 4
public Unit SetpUnit { get; set; } /// 5
public double SetpStep { get; set; } /// 6
public bool RoundToSetpStep { get; set; } /// 7
public int ReservoirNr { get; set; } /// 8
/// <summary> Procedure parameters </summary>
@ -66,6 +75,7 @@ namespace TBF.Rig.Modbus.TempControl.Novus
Name = "Novus";
ParentName = "Modbus";
ModbusAddress = 49;
Type = Novus.Type.N1200HC;
MsrdFormat = "{0:F1} °C";
MsrdUnit = Unit.C;
MsrdValLimLo = 5;
@ -80,13 +90,14 @@ namespace TBF.Rig.Modbus.TempControl.Novus
string[] paramNames = new string[]
{
"Modbus address", /// 0
"Measured temperature format", /// 1
"Unit of measured temperature", /// 2
"Setpoint format", /// 3
"Unit of temperature setpoint", /// 4
"Setpoint step", /// 5
"Round to setpoint step", /// 6
"Reservoir nr. (1-based)", /// 7
"Type", /// 1
"Measured temperature format", /// 2
"Unit of measured temperature", /// 3
"Setpoint format", /// 4
"Unit of temperature setpoint", /// 5
"Setpoint step", /// 6
"Round to setpoint step", /// 7
"Reservoir nr. (1-based)", /// 8
};
public string ParamName(int i) { return paramNames[i]; }
public int ParamsCount() { return paramNames.Length; }
@ -95,10 +106,14 @@ namespace TBF.Rig.Modbus.TempControl.Novus
{
switch (i)
{
case 2:
case 4:
case 1:
var list = new List<string>();
for (Novus.Type t = 0; t < Novus.Type.Count; t++) list.Add(t.ToString());
return list;
case 3:
case 5:
return new string[] { "°C", "°F", "K" };
case 6:
case 7:
return new string[] { Strings.yes, Strings.no };
default:
return null;
@ -111,15 +126,17 @@ namespace TBF.Rig.Modbus.TempControl.Novus
switch (i)
{
case 0: return ModbusAddress.ToString();
case 1: return MsrdFormat;
case 2: return (MsrdUnit == Unit.K) ? "K" : (MsrdUnit == Unit.F) ? "°F" : "°C";
case 3: return SetpFormat;
case 4: return (SetpUnit == Unit.K) ? "K" : (SetpUnit == Unit.F) ? "°F" : "°C";
case 5: return SetpStep.ToString();
case 6: return RoundToSetpStep ? Strings.yes : Strings.no;
case 7: return ReservoirNr.ToString();
case 1: return Type.ToString();
case 2: return MsrdFormat;
case 3: return (MsrdUnit == Unit.K) ? "K" : (MsrdUnit == Unit.F) ? "°F" : "°C";
case 4: return SetpFormat;
case 5: return (SetpUnit == Unit.K) ? "K" : (SetpUnit == Unit.F) ? "°F" : "°C";
case 6: return SetpStep.ToString();
case 7: return RoundToSetpStep ? Strings.yes : Strings.no;
case 8: return ReservoirNr.ToString();
default:
return string.Format("{0}({1}) address={2}", Name, string.IsNullOrEmpty(ParentName) ? "-" : ParentName, ModbusAddress);
return string.Format("{0}({1}) address = {2}, type = {3}, reservoir = {4}",
Name, string.IsNullOrEmpty(ParentName) ? "-" : ParentName, ModbusAddress, Type, ReservoirNr);
}
}
@ -128,19 +145,29 @@ namespace TBF.Rig.Modbus.TempControl.Novus
switch (i)
{
case 0: ModbusAddress = int.Parse(str); return CfgUpdateFlags.RestartRqrd;
case 1: MsrdFormat = str; return CfgUpdateFlags.RestartRqrd;
case 2: MsrdUnit = (str == "°K" || str == "K") ? Unit.K
case 1:
for (Novus.Type t = 0; t < Novus.Type.Count; t++)
{
if (t.ToString() == str)
{
Type = t;
return CfgUpdateFlags.RestartRqrd;
}
}
return CfgUpdateFlags.None;
case 2: MsrdFormat = str; return CfgUpdateFlags.RestartRqrd;
case 3: MsrdUnit = (str == "°K" || str == "K") ? Unit.K
: (str == "°F" || str == "F") ? Unit.F
: Unit.C;
return CfgUpdateFlags.RestartRqrd;
case 3: SetpFormat = str; return CfgUpdateFlags.RestartRqrd;
case 4: SetpUnit = (str == "°K" || str == "K") ? Unit.K
case 4: SetpFormat = str; return CfgUpdateFlags.RestartRqrd;
case 5: SetpUnit = (str == "°K" || str == "K") ? Unit.K
: (str == "°F" || str == "F") ? Unit.F
: Unit.C;
return CfgUpdateFlags.RestartRqrd;
case 5: SetpStep = Utils.ParseUDouble(str); return CfgUpdateFlags.RestartRqrd;
case 6: RoundToSetpStep = (str == Strings.yes); return CfgUpdateFlags.RestartRqrd;
case 7: ReservoirNr = int.Parse(str); return CfgUpdateFlags.RestartRqrd;
case 6: SetpStep = Utils.ParseUDouble(str); return CfgUpdateFlags.RestartRqrd;
case 7: RoundToSetpStep = (str == Strings.yes); return CfgUpdateFlags.RestartRqrd;
case 8: ReservoirNr = int.Parse(str); return CfgUpdateFlags.RestartRqrd;
default: return CfgUpdateFlags.None;
}
}
@ -154,22 +181,29 @@ namespace TBF.Rig.Modbus.TempControl.Novus
switch (i)
{
case 0:
/// ModbusAddress
if (int.TryParse(str, out idummy) && idummy >= 1 && idummy <= 254) return true;
break;
case 1:
case 3:
return true;
case 7:
/// Type, RoundToSetpStep (yes/no)
if (ParamValues(i).Contains(str)) return true;
break;
case 2:
case 4:
if (str == "°K" || str == "K" || str == "°F" || str == "F" || str == "°C" || str == "C") return true;
break;
/// MsrdFormat, SetpFormat
return true;
case 3:
case 5:
if (Utils.TryParseUDouble(str, out ddummy)) return true;
/// MsrdUnit, SetpUnit
if (ParamValues(i).Contains(str) || str == "C" || str == "F" || str == "°K") return true;
break;
case 6:
if (str == Strings.yes || str == Strings.no) return true;
/// SetpStep
if (Utils.TryParseUDouble(str, out ddummy)) return true;
break;
case 7:
case 8:
/// ReservoirNr
if (int.TryParse(str, out idummy) && idummy >= 1 && idummy <= 10) return true;
break;
default:
@ -185,6 +219,7 @@ namespace TBF.Rig.Modbus.TempControl.Novus
{
prms.ParentName = ParentName;
prms.ModbusAddress = ModbusAddress;
prms.Type = Type;
prms.MsrdFormat = MsrdFormat;
prms.MsrdUnit = MsrdUnit;
prms.MsrdValLimLo = MsrdValLimLo;