Files
tbf/TBF/Rig/ControlBoard/Uni/OutputMessage.cs
T

210 lines
7.5 KiB
C#

///
/// Copyright (c) 2021 Sensus Slovensko a.s.
///
using System;
using System.Diagnostics;
using log4net;
using Dirichlet.Numerics;
namespace TBF.Rig.ControlBoard.Uni
{
public static class OutputMessage
{
private static readonly ILog log = LogManager.GetLogger(typeof(OutputMessage));
public const int PayloadLen = 38;
public const byte MessageCode = (byte)' ';
static Action arguments = new Action();
///
static void FetchArguments(Action combinedAction)
{
foreach (var aId in (ActionID[])Enum.GetValues(typeof(ActionID)))
{
if ((combinedAction.ActionId & aId) != 0) arguments.SetArgs(combinedAction, aId);
}
}
public static byte[] GetMessage(Action action, short config, ulong bitsToInvert)
{
FetchArguments(action);
byte[] message = new byte[PayloadLen + 5]; /// headed 3 bytes, checksum 2 bytes
/// Header + message code (4 bytes)
message[0] = Const.STX;
message[1] = 1; /// Control board addres
message[2] = (byte)PayloadLen; /// Payload length
message[3] = MessageCode;
/// Command
message[4] = GetCommand(action);
message[5] = action.IsStart() ? GetStartParams(action) : action.IsStop() ? GetStopParams(action) : (byte)0;
message[6] = (arguments.DivId <= 0) ? (byte)0 : (byte)(arguments.DivId - 1);
int totalPulsesCount = (arguments.TotalPulsesCount < 0) ? 0 : arguments.TotalPulsesCount;
message[7] = (byte)(totalPulsesCount & 0xFF);
message[8] = (byte)((totalPulsesCount >> 8) & 0xFF);
message[9] = (byte)((totalPulsesCount >> 16) & 0xFF);
int massPulsesCount = (arguments.MassPulsesCount < 0) ? 0 : arguments.MassPulsesCount;
message[10] = (byte)(massPulsesCount & 0xFF);
message[11] = (byte)((massPulsesCount >> 8) & 0xFF);
message[12] = (byte)((massPulsesCount >> 16) & 0xFF);
message[13] = 0;
message[14] = (byte)(config & 0xff);
message[15] = (byte)((config >> 8) & 0xff);
/// Route
ulong routeOut = arguments.Route ^ bitsToInvert;
message[16] = ((action.ActionId & ActionID.ChangeRoute) != 0) ? (byte)1 : (byte)0; /// Route change flag
message[17] = (byte)(routeOut & 0xFF);
message[18] = (byte)((routeOut >> 8) & 0xFF);
message[19] = (byte)((routeOut >> 16) & 0xFF);
message[20] = (byte)((routeOut >> 24) & 0xFF);
message[21] = (byte)((routeOut >> 32) & 0xFF);
message[22] = (byte)((routeOut >> 40) & 0xFF);
message[23] = (byte)((routeOut >> 48) & 0xFF);
message[24] = (byte)((routeOut >> 56) & 0xFF);
/// DA2
message[25] = 0;
message[26] = 0;
/// Regulation valve
message[27] = (action.RegVId < 0) ? (byte)0 : (byte)(action.RegVId | (int)action.RegVMode);
message[28] = (byte)(action.RVMovePar1 & 0xFF);
message[29] = (byte)((action.RVMovePar1 >> 8) & 0xFF);
message[30] = (byte)(action.RVMovePar2 & 0xFF);
message[31] = (byte)((action.RVMovePar2 >> 8) & 0xFF);
message[32] = GetRVStopParam(action);
/// DA1
message[33] = 0; /// PrudParL
message[34] = 0; /// PrudParH
message[35] = (byte)Math.Max(1, Math.Min(255, arguments.PID)); /// PIDRV
message[36] = 0; // (byte)Math.Max(4, Math.Min(255, action.TolerRV)); /// TolerRV (4..20 mA reg.valve only)
message[37] = (byte)Math.Max(0, Math.Min(255, arguments.StabTime)); /// StabRV
message[38] = (byte)((arguments.DivThreshold >> 2) & 0xFF);
message[39] = 0;
message[40] = 0;
Debug.Assert(40 == PayloadLen + 2);
/// Checksum (2 bytes)
int checksum = 0;
for (int i = 1; i < PayloadLen + 3; i++) checksum += message[i];
message[PayloadLen + 3] = (byte)(checksum & 0xFF);
message[PayloadLen + 4] = (byte)((checksum >> 8) & 0xFF);
return message;
}
static byte GetCommand(Action action)
{
if (action.StartStop == Command.DelayStartOrStop)
{
return (byte)Command.None;
}
else if (action.StartStop == Command.Start && action.IsProlonged)
{
return (byte)(Command.Start | Command.ProlongedFlag);
}
else
{
return (byte)action.StartStop;
}
}
static byte GetStartParams(Action action)
{
int param = action.FlowMId & (int)StartParam.EtalonMask;
if (action.IsDivUsed) param |= (int)StartParam.DiverterStart;
if (action.IsSyncMethod) param |= (int)StartParam.SynchroMethod;
if (action.IsStartStop) param |= (int)StartParam.StartStopMethod;
return (byte)param;
}
static byte GetStopParams(Action action)
{
return (byte)StopDevs.All;
}
static byte GetRVStopParam(Action action)
{
if (action.IsStop()) return (byte)0x20;
if ((action.ActionId & ActionID.RegVlvStop) != 0) return (byte)0x20;
return (byte)0;
}
}
public enum Command : byte
{
None = 0, /// Request data only
Start = 1,
Stop = 2,
GetDiverterTransitionData = 4,
GetScopeAnalyzerData = 7,
ResetScopeAnalyzer = 8,
GetSwitchCounterData = 9,
///
ProlongedFlag = 0x10, /// Not a command of its own, a valid command is (byte)(Command.Start | Command.ProlongedFlag)
DelayStartOrStop = 255, /// Prevents other start or stop command, results in Command.None when creating a message
}
public enum StartParam : byte
{
EtalonMask = 0x07,
DiverterStart = 0x08,
SynchroMethod = 0x10,
DirectGateStart = 0x20, /// ???
StartStopMethod = 0x40,
CalibrationMode = 0x80,
}
public enum StopDevs : byte
{
None = 0,
Reference = 0x01, /// bit 0
Diverter = 0x02, /// bit 1
GatePulse = 0x08, /// bit 3
TimeMeasurement = 0x10, /// bit 4
BypassDevCoupled2Div = 0x20, /// bit 5
RegValveRegulation = 0x80, /// bit 7
All = 0x9B,
}
public enum ValveChgFlag : byte
{
DoNotChange = 0,
Change = 1,
SendPressureMeterAddresses = 2,
}
public enum RegValveMode : byte
{
None = 0,
TargetFrequency = 0x10, /// Low and high frequency limits (0..2000Hz,0..2200Hz) are specified
TargetPosition = 0x20, /// Low and high position (=DAC value) limits (0..100%) are specified
Stop = 0x30, /// Stop regulation
PulseWidth = 0x40, /// Pulse width 0.05 .. 2 sec. ... opening and closing (opening=0x40, closing=0xC0)
NegPulseWidth = 0x80, /// Pulse width 0.05 .. 2 sec. ... closing (closing=0xC0)
}
public enum RegValveState : byte
{
Idle = 0,
PwOrFreqRegul = 2,
DacValueRegul = 3,
}
}