328 lines
9.9 KiB
C#
328 lines
9.9 KiB
C#
namespace Common.Hardware.WaterMeter.eRegister
|
|
{
|
|
using Common.Hardware.Interfaces.Ports.SIRT;
|
|
using Common.Hardware.Interfaces.Ports.SIRT.Parameters;
|
|
using Common.Hardware.WaterMeter.eRegister.Data;
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
public class ProgrammingTask : SirtTask
|
|
{
|
|
private const String PENDING = nameof(PENDING);
|
|
|
|
private readonly ICollection<LAT> lats = new List<LAT>();
|
|
private readonly ICollection<BUP> bups = new List<BUP>();
|
|
private readonly ICollection<DEBUG> debugs = new List<DEBUG>();
|
|
private readonly ICollection<SEMI> semis = new List<SEMI>();
|
|
|
|
public event Action<ProgrammingTask> OnProgress;
|
|
|
|
public Byte[] EncryptionKey { get; set; }
|
|
|
|
public Int32 Slotno { get; internal set; }
|
|
|
|
public Int32 Rowno { get; internal set; }
|
|
|
|
public String Label { get; private set; }
|
|
|
|
public String RSSI { get; private set; }
|
|
|
|
public TaskState State { get; private set; } = TaskState.None;
|
|
|
|
public String PamNames { get; internal set; }
|
|
|
|
public override void PrepareForRun()
|
|
{
|
|
this.OnProgress?.Invoke(this);
|
|
|
|
base.PrepareForRun();
|
|
}
|
|
|
|
public override void ReceiveMessage(SirtMessage response, Int32 mhz)
|
|
{
|
|
if (this.EncryptionKey != null && this.EncryptionKey.Length == 16)
|
|
{
|
|
this.DecryptResponse(response);
|
|
}
|
|
|
|
if (this.State != TaskState.Success && response.Cmd == SirtMessage.FROMAIR)
|
|
{
|
|
var label = this.Label;
|
|
var state = this.State;
|
|
var data = response.Data;
|
|
var type = data[0];
|
|
|
|
if (type == SirtMessage.SEMI)
|
|
{
|
|
var semi = new SEMI(data, (UInt16)mhz);
|
|
label = semi.PAMStatus.ToString();
|
|
|
|
if (semi.PAMStatus == PAMStatus.OK)
|
|
{
|
|
state = TaskState.Success;
|
|
this.TaskStatus = TaskStatus.Completed;
|
|
}
|
|
else
|
|
{
|
|
state = TaskState.Error;
|
|
this.TaskStatus = TaskStatus.Cancelled;
|
|
}
|
|
|
|
this.semis.Add(semi);
|
|
}
|
|
else if (type == SirtMessage.BUP)
|
|
{
|
|
label = nameof(SirtMessage.BUP);
|
|
state = TaskState.Warning;
|
|
|
|
this.bups.Add(new BUP(data));
|
|
}
|
|
else if (type == SirtMessage.LAT)
|
|
{
|
|
label = nameof(SirtMessage.LAT);
|
|
state = TaskState.Warning;
|
|
|
|
this.lats.Add(new LAT(data));
|
|
}
|
|
else if (type == SirtMessage.DEBUG)
|
|
{
|
|
label = nameof(SirtMessage.DEBUG);
|
|
state = TaskState.Warning;
|
|
|
|
this.debugs.Add(new DEBUG(data));
|
|
}
|
|
|
|
this.Label = label;
|
|
this.State = state;
|
|
this.RSSI = SEMI.CalculateRSSI(response.P2, (UInt16)mhz);
|
|
}
|
|
|
|
this.OnProgress?.Invoke(this);
|
|
base.ReceiveMessage(response, mhz);
|
|
}
|
|
|
|
public override Byte[] ToByteArray()
|
|
{
|
|
if (this.EncryptionKey != null && this.EncryptionKey.Length == 16)
|
|
{
|
|
this.EncryptRequest();
|
|
}
|
|
|
|
return base.ToByteArray();
|
|
}
|
|
|
|
internal void Refresh()
|
|
{
|
|
var semi = this.semis.LastOrDefault();
|
|
|
|
if (semi != null)
|
|
{
|
|
this.Label = semi.PAMStatus.ToString();
|
|
|
|
if (semi.PAMStatus == PAMStatus.OK)
|
|
{
|
|
this.State = TaskState.Success;
|
|
}
|
|
else
|
|
{
|
|
this.State = TaskState.Error;
|
|
}
|
|
}
|
|
else if (this.LastPayload != null)
|
|
{
|
|
var last = new SirtMessage(this.LastPayload);
|
|
P11 p11 = last.P1;
|
|
|
|
if (p11.LenOverflow)
|
|
{
|
|
this.Label = "LEN OVERFLOW";
|
|
this.State = TaskState.Error;
|
|
}
|
|
else if (!p11.SrcChannel && p11.InvalidMod1)
|
|
{
|
|
this.Label = "MOD 1 CRC ERROR";
|
|
this.State = TaskState.Error;
|
|
}
|
|
else if (p11.SrcChannel && p11.InvalidMod2)
|
|
{
|
|
this.Label = "MOD 2 CRC ERROR";
|
|
this.State = TaskState.Error;
|
|
}
|
|
else
|
|
{
|
|
this.Label = nameof(PAMStatus.OK);
|
|
this.State = TaskState.Success;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this.Label = "NA";
|
|
this.State = TaskState.Error;
|
|
}
|
|
|
|
this.OnProgress?.Invoke(this);
|
|
}
|
|
|
|
internal Tuple<String, TaskState> CheckMBusStatus(ParameterSet parameterSet)
|
|
{
|
|
var label = $"{this.semis.LastOrDefault()?.MBusStatus} OK";
|
|
|
|
return new Tuple<String, TaskState>(label, TaskState.Success);
|
|
}
|
|
|
|
internal Tuple<String, TaskState> CheckBatteryVoltage(ParameterSet parameterSet)
|
|
{
|
|
var label = $"{this.debugs.LastOrDefault()?.BatteryVoltage:0.00} OK";
|
|
|
|
return new Tuple<String, TaskState>(label, TaskState.Success);
|
|
}
|
|
|
|
internal Tuple<String, TaskState> CheckBatteryRemaining(ParameterSet parameterSet)
|
|
{
|
|
var label = $"{this.semis.LastOrDefault()?.BatteryRemaining:0.0} OK";
|
|
|
|
return new Tuple<String, TaskState>(label, TaskState.Success);
|
|
}
|
|
|
|
internal Tuple<String, TaskState> CheckRadioFWVersion(ParameterSet parameterSet)
|
|
{
|
|
var label = $"{this.debugs.LastOrDefault()?.RadioFirmwareVersion} OK";
|
|
|
|
return new Tuple<String, TaskState>(label, TaskState.Success);
|
|
}
|
|
|
|
internal Tuple<String, TaskState> CheckMetrologyFWVersion(ParameterSet arg)
|
|
{
|
|
var label = $"{this.debugs.LastOrDefault()?.MetrologyFirmwareVersion} OK";
|
|
|
|
return new Tuple<String, TaskState>(label, TaskState.Success);
|
|
}
|
|
|
|
internal Tuple<String, TaskState> CheckReboots(ParameterSet parameterSet)
|
|
{
|
|
var label = $"{this.debugs.LastOrDefault()?.RebootCount} OK";
|
|
|
|
return new Tuple<String, TaskState>(label, TaskState.Success);
|
|
}
|
|
|
|
private void DecryptResponse(SirtMessage response)
|
|
{
|
|
var data = response.Data;
|
|
var length = data.Length;
|
|
|
|
if (length < 13)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var bytes = new Byte[length];
|
|
var hex = BitConverter.ToString(bytes);
|
|
|
|
Array.Copy(data, 0, bytes, 0, length);
|
|
|
|
var token = bytes[1];
|
|
|
|
for (var x = 6; x < length; x++)
|
|
{
|
|
token += (Byte)(bytes[x - (token & 0x3) - 1] >> ((token >> 6) & 0x1));
|
|
|
|
bytes[x] ^= this.EncryptionKey[(token >> 2) & 0x0F];
|
|
|
|
if (token >= 0x80)
|
|
{
|
|
bytes[x] = (Byte)~bytes[x];
|
|
}
|
|
}
|
|
|
|
var crc = this.CalculateCRC(bytes, 5, length - 2);
|
|
|
|
if (crc == 0 || crc == UInt16.MaxValue)
|
|
{
|
|
response.Data = bytes;
|
|
}
|
|
}
|
|
|
|
private void EncryptRequest()
|
|
{
|
|
var data = this.Data;
|
|
var dataLength = data.Length;
|
|
var telegramLength = dataLength + 2;
|
|
var telegram = new Byte[telegramLength];
|
|
|
|
Array.Copy(data, 0, telegram, 0, dataLength);
|
|
|
|
var telegramForCRCLength = telegramLength + 4;
|
|
var telegramForCRC = new Byte[telegramForCRCLength];
|
|
var address = this.Address;
|
|
|
|
telegramForCRC[0] = (Byte)(address >> 24);
|
|
telegramForCRC[1] = (Byte)(address >> 16);
|
|
telegramForCRC[2] = (Byte)(address >> 8);
|
|
telegramForCRC[3] = (Byte)(address >> 0);
|
|
|
|
Array.Copy(telegram, 0, telegramForCRC, 4, telegramLength);
|
|
|
|
var telegramCRC = this.CalculateCRC(telegramForCRC, 1, telegramForCRCLength - 2);
|
|
|
|
telegram[telegramLength - 2] = (Byte)(telegramCRC >> 8);
|
|
telegram[telegramLength - 1] = (Byte)(telegramCRC >> 0);
|
|
|
|
var reference = address;
|
|
var token = (Byte)0xFF;
|
|
|
|
for (var i = 0; i < telegramLength; i++)
|
|
{
|
|
token += (Byte)(((reference >> ((token & 0x3) << 3)) & 0xFF) >> ((token >> 6) & 1));
|
|
|
|
reference = reference << 8;
|
|
reference += telegram[i];
|
|
|
|
telegram[i] ^= this.EncryptionKey[(token >> 2) & 0x0F];
|
|
|
|
if (token >= 0x80)
|
|
{
|
|
telegram[i] = (Byte)~telegram[i];
|
|
}
|
|
}
|
|
|
|
this.Data = telegram;
|
|
}
|
|
|
|
private UInt16 CalculateCRC(Byte[] data, Int32 start, Int32 length)
|
|
{
|
|
var crc = (UInt16)0xFFFF;
|
|
var dataLength = data?.Length ?? 0;
|
|
|
|
if (start + length > dataLength)
|
|
{
|
|
return crc;
|
|
}
|
|
|
|
for (var b = start; b < length; b++)
|
|
{
|
|
var _byte = data[b];
|
|
var token = (UInt16)((_byte << 8) & 0xFF00);
|
|
|
|
for (var i = 0; i < 8; i++)
|
|
{
|
|
if (0x8000 == ((crc ^ token) & 0x8000))
|
|
{
|
|
crc <<= 1;
|
|
crc ^= 0x1021;
|
|
}
|
|
else
|
|
{
|
|
crc <<= 1;
|
|
}
|
|
|
|
token <<= 1;
|
|
}
|
|
}
|
|
|
|
return crc;
|
|
}
|
|
}
|
|
}
|