progress over omnis

This commit is contained in:
Stoyan Zlatev
2023-11-29 09:20:28 +01:00
parent 38486e0e5d
commit a3c59f9847
60 changed files with 7757 additions and 610 deletions
+14 -5
View File
@@ -26,7 +26,7 @@ namespace OmniPlus
var response = new byte[0];
var received = false;
if (command is null)
if (command?.Length <= 0)
{
return response;
}
@@ -36,22 +36,31 @@ namespace OmniPlus
response = bytes
.FromIrdaMessage()
.FromUI1236Message();
received = true;
this.InvokeLogMessage($"RX: {BitConverter.ToString(response)}");
received = response.Length > 0 && response[0] > 0;
if (received)
{
this.MessageReceived -= MessageReceivedHandler;
}
}
this.MessageReceived += MessageReceivedHandler;
this.Write(1, 1, 1, 1);
var request = command
.ToUI1236Message()
.ToIrdaMessage();
var sleep = 5000;
var delay = 500;
this.Write(1, 1, 1, 1);
Thread.Sleep(1);
this.Write(request);
this.InvokeLogMessage($"TX: {BitConverter.ToString(command)}");
while (!received && sleep >= 0)
while (!received /*&& sleep >= 0*/)
{
sleep -= delay;
@@ -5,6 +5,7 @@ using OmniPlus.Options;
using System;
using System.IO;
using System.IO.Ports;
using System.Threading;
namespace OmniPlus
{
@@ -16,6 +17,7 @@ namespace OmniPlus
private SerialPort serialPort;
private SerialPortState state;
private Thread readingThread;
protected SerialPortConnection()
=> this.Options = new SerialPortOptions();
@@ -26,11 +28,20 @@ namespace OmniPlus
public virtual void Close()
{
try
{
this.readingThread?.Abort();
}
catch (Exception e)
{
this.LogMessage?.Invoke(e.ToString());
}
try
{
if (this.serialPort != null)
{
this.serialPort.DataReceived -= this.SerialPort_DataReceived;
// this.serialPort.DataReceived -= this.SerialPort_DataReceived;
this.serialPort.Disposed -= this.SerialPort_Disposed;
this.serialPort.ErrorReceived -= this.SerialPort_ErrorReceived;
this.serialPort.PinChanged -= this.SerialPort_PinChanged;
@@ -42,6 +53,7 @@ namespace OmniPlus
catch (Exception e)
{
this.StateChanged?.Invoke(SerialPortState.ErrorReceived, e.ToString());
this.LogMessage?.Invoke(e.ToString());
}
this.serialPort = null;
@@ -64,9 +76,9 @@ namespace OmniPlus
{
foreach (var @delegate in this.StateChanged.GetInvocationList())
{
if (@delegate is LogMessageEventHandler eventHandler)
if (@delegate is StateChangedEventHandler eventHandler)
{
this.LogMessage -= eventHandler;
this.StateChanged -= eventHandler;
}
}
}
@@ -75,9 +87,9 @@ namespace OmniPlus
{
foreach (var @delegate in this.DataReceived.GetInvocationList())
{
if (@delegate is LogMessageEventHandler eventHandler)
if (@delegate is DataReceivedHandler eventHandler)
{
this.LogMessage -= eventHandler;
this.DataReceived -= eventHandler;
}
}
}
@@ -101,6 +113,7 @@ namespace OmniPlus
this.state = SerialPortState.ErrorReceived;
this.StateChanged?.Invoke(this.state, $"{this.Options.PortName}: {e}");
this.LogMessage?.Invoke(e.ToString());
}
try
@@ -116,7 +129,7 @@ namespace OmniPlus
DtrEnable = this.Options.DtrEnabled
};
this.serialPort.DataReceived += this.SerialPort_DataReceived;
// this.serialPort.DataReceived += this.SerialPort_DataReceived;
this.serialPort.Disposed += this.SerialPort_Disposed;
this.serialPort.ErrorReceived += this.SerialPort_ErrorReceived;
this.serialPort.PinChanged += this.SerialPort_PinChanged;
@@ -133,34 +146,106 @@ namespace OmniPlus
+ $", {this.Options.DataBits}"
+ $", {this.Options.Parity}"
+ $", {this.Options.StopBits}");
if (this.serialPort.IsOpen)
{
this.readingThread = new Thread(this.SerialPortConnectionBeginRead);
this.readingThread.Start(this.serialPort.BaseStream);
}
}
catch (Exception e)
{
this.state = SerialPortState.ErrorReceived;
this.StateChanged?.Invoke(this.state, $"{this.Options.PortName}: {e}");
this.LogMessage?.Invoke(e.ToString());
this.Close();
}
}
protected void InvokeLogMessage(string message)
=> this.LogMessage?.Invoke(message);
protected void Write(params byte[] message)
{
if (this.Connected && message.Length > 0)
{
this.serialPort.Write(message, 0, message.Length);
lock (this.serialPort)
{
this.serialPort.Write(message, 0, message.Length);
}
}
this.LogMessage?.Invoke($"TX: {BitConverter.ToString(message)}");
}
protected abstract void SerialPortConnection_DataReceived(byte[] bytes);
private void SerialPortConnectionBeginRead(object parameter)
{
if (parameter is Stream stream && stream.CanRead)
{
byte[] data;
byte[] buffer = new byte[128];
AsyncCallback endReadHandler = _asyncResult =>
{
try
{
if (_asyncResult.AsyncState is Stream _stream)
{
var bytesReaded = _stream.EndRead(_asyncResult);
if (bytesReaded > 0)
{
data = new byte[bytesReaded];
Array.Copy(buffer, 0, data, 0, bytesReaded);
this.DataReceived?.Invoke(buffer);
}
}
}
catch (Exception e)
{
this.LogMessage?.Invoke(e.ToString());
}
};
while (stream.CanRead)
{
stream
.BeginRead(buffer, 0, buffer.Length, endReadHandler, stream)
.AsyncWaitHandle
.WaitOne();
}
}
this.Close();
}
protected void SerialPortConnection_LogMessage(string message)
=> File.AppendAllText(this.Options.LogFile, $"{message}{Environment.NewLine}");
{
try
{
File.AppendAllText(this.Options.LogFile, $"{message}{Environment.NewLine}");
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
protected void SerialPortConnection_StateChanged(SerialPortState state, string message)
=> File.AppendAllText(this.Options.LogFile, $"[{state}] {message}{Environment.NewLine}");
{
try
{
File.AppendAllText(this.Options.LogFile, $"[{state}] {message}{Environment.NewLine}");
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
private void SerialPort_PinChanged(object sender, SerialPinChangedEventArgs args)
{
@@ -189,21 +274,21 @@ namespace OmniPlus
this.Close();
}
private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs args)
{
if (this.Connected && sender is SerialPort port && port.BytesToRead > 0)
{
var bytes = new byte[port.BytesToRead];
var lock_object = new object();
/// <summary>
/// TODO: investigate the usage of port.Read... (may be its wrong)
/// New approach implemented whit the base stream. See above!
/// </summary>
//private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs args)
//{
// if (this.Connected && sender is SerialPort port)
// {
// var bytes = new byte[port.BytesToRead];
lock (lock_object)
{
port.Read(bytes, 0, port.BytesToRead);
}
this.DataReceived?.Invoke(bytes);
this.LogMessage?.Invoke($"RX: {BitConverter.ToString(bytes)}");
}
}
// port.Read(bytes, 0, port.BytesToRead);
// this.DataReceived?.Invoke(bytes);
// this.LogMessage?.Invoke($"RX: {BitConverter.ToString(bytes)}");
// }
//}
}
}
@@ -40,8 +40,8 @@ namespace OmniPlus
this.MessageReceived += MessageReceivedHandler;
var request = command.ToUniProMessage();
var sleep = 5000;
var delay = 500;
var sleep = 33000;
var delay = 1000;
this.Write(request);
+57 -9
View File
@@ -1,14 +1,32 @@
namespace OmniPlus.Features
{
using System.Text;
using System;
using static System.Text.Encoding;
namespace OmniPlus.Features
{
public class ASCIIString : FeatureRequest
{
protected const char NULL = '\0';
public ASCIIString(int length) : base(length)
{
}
public ASCIIString(string value, int length) : base(length)
{
if (!string.IsNullOrWhiteSpace(value))
{
value = value.Trim(NULL);
if (value.Length > length - 2)
{
value = value.Substring(0, length - 2);
}
this.Value = $"{NULL}{value}{NULL}";
}
}
public ASCIIString(byte[] response, int length) : base(response, length)
{
@@ -20,18 +38,48 @@
set => this.SetValue(value);
}
public void ToASCIIString(byte[] buffer, int start, int end)
{
var value = string.Empty;
while (start <= end)
{
value += (char)buffer[start];
start++;
}
this.SetValue(value);
}
private string GetValue()
=> Encoding.ASCII
.GetString(this.bytes)
.Trim();
{
if (this.bytes?.Length > 0)
{
return ASCII
.GetString(this.bytes)
.Trim('\0');
}
return default(string);
}
private void SetValue(string value)
{
this.bytes.Clear();
Encoding.ASCII
.GetBytes(value?.Trim('\0', ' ') ?? string.Empty)
.CopyTo(this.bytes, 0);
if (!string.IsNullOrWhiteSpace(value))
{
var buffer = ASCII.GetBytes(value);
var bufferLength = buffer.Length;
var i = 0;
while (i < this.length && i < bufferLength)
{
this.bytes[i] = buffer[i];
i++;
}
}
}
}
}
+7 -5
View File
@@ -16,10 +16,12 @@
{
}
public static implicit operator BuildInfo(string value)
=> new BuildInfo
{
Value = value,
};
public BuildInfo(string value) : base(value, LENGTH)
{
}
public static implicit operator BuildInfo(string value)
=> new BuildInfo { Value = value };
}
}
@@ -1,68 +0,0 @@
using System;
namespace OmniPlus.Features
{
public class ChamberCalibration : FeatureRequest
{
public const byte ACTIVATE_TEST = 0x00;
public const byte REQUEST_RESULTS = 0x01;
public const byte TEST_ACTIVATED = 0x00;
public const byte TEST_FAILED = 0x01;
public const byte TEST_COMPLETED = 0x02;
const int LENGTH = 5;
public ChamberCalibration() : base(LENGTH)
{
}
public ChamberCalibration(byte[] response) : base(response, LENGTH)
{
}
/// <summary>
/// Request Response
/// 0 – Activate Factory Calibration Test Test Activation Successful
/// 1 – Request Factory Calibration Test Results Test Failed (Timeout)
/// 2 - ........................................ Test Successfully Completed
/// </summary>
public byte Control
{
get => this.bytes[0];
set => this.bytes[0] = value;
}
/// <summary>
/// <para>
/// Request: Number of Rotations to collect. (not required if Requesting Calibration results) (Limited to 1 to 5000 rotations)
/// </para>
/// <para>
/// Response: Number of rotations collected
/// On Test Activation, this field echoes the number of rotations requested.
/// On Test Failed and Test Successfully Completed, returns the number of rotations collected.
/// </para>
/// </summary>
public ushort Rotations
{
get => BitConverter.ToUInt16(this.bytes, 1);
set => BitConverter.GetBytes(value).CopyTo(this.bytes, 1);
}
/// <summary>
/// <para>
/// Request: Maximum Time command is supposed to take to execute - 1 to 3600 Seconds.
/// </para>
/// <para>
/// Response: Total Time it took to collect the indicated rotations. Each count is equal to 30.5176us.
/// On Test Activation Successful, returns number of seconds test is allowed to take.
/// On Test Failed, this field will show 0 indicating that the tested failed.
/// </para>
/// </summary>
public ushort Duration
{
get => BitConverter.ToUInt16(this.bytes, 3);
set => BitConverter.GetBytes(value).CopyTo(this.bytes, 3);
}
}
}
@@ -1,6 +1,7 @@
namespace OmniPlus.Features
{
using System;
using System.Text;
public class ConfigurationParameters : FeatureRequest
{
@@ -20,11 +21,11 @@
{
this.ProgrammableID = new ProgrammableID();
this.ProgrammableID.Status = this.Status;
this.ProgrammableID.Value = this.bytes.ToASCIIString(0, 20);
this.ProgrammableID.ToASCIIString(this.bytes, 0, 20);
this.FactoryID = new FactoryID();
this.FactoryID.Status = this.Status;
this.FactoryID.Value = this.bytes.ToASCIIString(21, 33);
this.FactoryID.ToASCIIString(this.bytes, 21, 33);
this.ReadingDigits = new ReadingDigits();
this.ReadingDigits.Status = this.Status;
@@ -33,18 +34,18 @@
this.Readings = new Readings();
this.Readings.Status = this.Status;
this.Readings.Value = this.bytes.ToASCIIString(36, 45);
this.Readings.ToASCIIString(this.bytes, 36, 45);
this.OptionalUniDirFields = new OptionalUniDirFields();
this.OptionalUniDirFields.Status = this.Status;
this.OptionalUniDirFields.Value = this.bytes.ToASCIIString(46, 49);
this.OptionalUniDirFields.ToASCIIString(this.bytes, 46, 49);
this.AlartPersistenceDays = this.bytes[50];
this.AlarmPersistenceDays = this.bytes[50];
this.DataLogIntervalMins = BitConverter.ToUInt16(this.bytes, 51);
this.DeviceID = new DeviceID();
this.DeviceID.Status = this.Status;
this.DeviceID.Value = this.bytes.ToASCIIString(53, 61);
this.DeviceID.ToASCIIString(this.bytes, 53, 61);
}
public ProgrammableID ProgrammableID { get; set; }
@@ -57,18 +58,221 @@
public OptionalUniDirFields OptionalUniDirFields { get; set; }
public byte AlartPersistenceDays { get; set; }
public byte AlarmPersistenceDays { get; set; }
public ushort DataLogIntervalMins { get; set; }
public DeviceID DeviceID { get; set; }
}
public ManifacturingTime ManifacturingTime { private get; set; }
public class ConfigurationParametersSet : FeatureRequest
{
const int LENGTH = 85;
public SystemTime SystemTime { private get; set; }
public ConfigurationParametersSet() : base(LENGTH)
{
}
public byte RebootCount { private get; set; }
private ConfigurationParametersSet(byte[] response) : base(response, LENGTH)
{
}
public BuildInfo BuildInfo { private get; set; }
/// <summary>
/// <para>0 - 20</para>
/// Set Programmable ID request, string is padded and terminated with '\0'
/// +1
/// </summary>
private string programmableID;
public string ProgrammableID
{
get => this.programmableID;
set
{
this.SetASCIIBytes(value, 0, 20);
this.programmableID = this.bytes.ToASCIIString(0, 20);
}
}
/// <summary>
/// <para>21 - 33</para>
/// Set Factory ID request, string padded and terminated with '\0'
/// +1
/// </summary>
private string factoryID;
public string FactoryID
{
get => this.factoryID;
set
{
this.SetASCIIBytes(value, 21, 13);
this.factoryID = this.bytes.ToASCIIString(21, 13);
}
}
/// <summary>
/// <para>34 - 35</para>
/// Set Number of Reading Digits request
/// </summary>
private sbyte nrOfReadingDigits;
public sbyte NumberOfReadingDigits
{
get => this.nrOfReadingDigits;
set
{
BitConverter.GetBytes(value).CopyTo(this.bytes, 34);
this.nrOfReadingDigits = (sbyte)BitConverter.ToInt16(this.bytes, 34);
}
}
/// <summary>
/// <para>36 - 47</para>
/// Set Preset Reading (v1.24), string padded and terminated with '\0'.
/// First Two Characters are set to 0x00 0x00 to indicate that we are programming Totalizer
/// +1
/// </summary>
private string readingPreset;
public string ReadingPreset
{
get => this.readingPreset;
set
{
this.SetASCIIBytes(value, 38, 10);
this.readingPreset = this.bytes.ToASCIIString(38, 10);
}
}
/// <summary>
/// <para>48 - 49</para>
/// Set Optional Uni-directional Fields request
/// </summary>
private ushort optionalFields;
public ushort OptionalUniDirFields
{
get => this.optionalFields;
set
{
BitConverter.GetBytes(value).CopyTo(bytes, 48);
this.optionalFields = BitConverter.ToUInt16(this.bytes, 48);
}
}
/// <summary>
/// <para>50 - 51</para>
/// Set Alarm Persistence Period request. First Byte is always set to 0x00 to indicate Volume Sensor
/// </summary>
public byte AlarmPercistancePeriodDays
{
get => this.bytes[51];
set => this.bytes[51] = value;
}
/// <summary>
/// <para>52 - 54</para>
/// Set Data Log Interval request. First Byte is always set to 0x00 to indicate Volume Sensor
/// </summary>
private ushort dataLogInterval;
public ushort DataLogIntervalMinutes
{
get => this.dataLogInterval;
set
{
BitConverter.GetBytes(value).CopyTo(this.bytes, 53);
this.dataLogInterval = BitConverter.ToUInt16(this.bytes, 53);
}
}
/// <summary>
/// <para>55 - 58</para>
/// Manufacturing Time request
/// </summary>
private uint manifactured;
public uint ManifacturingTime
{
get => this.manifactured;
set
{
BitConverter.GetBytes(value).CopyTo(this.bytes, 55);
this.manifactured = BitConverter.ToUInt16(this.bytes, 55);
}
}
/// <summary>
/// <para>59 - 62</para>
/// System Time request
/// </summary>
private uint timeDrift;
public uint SystemTime
{
get => this.timeDrift;
set
{
BitConverter.GetBytes(value).CopyTo(this.bytes, 59);
this.timeDrift = BitConverter.ToUInt16(this.bytes, 59);
}
}
/// <summary>
/// <para>63</para>
/// Set Reboot Count request
/// </summary>
public byte RebootCount
{
get => this.bytes[63];
set => this.bytes[63] = value;
}
/// <summary>
/// <para>64-79</para>
/// Build Information request, string padded and terminated with '\0'
/// +1
/// </summary>
private string buildInfo;
public string BuildInfo
{
get => this.buildInfo;
set
{
this.SetASCIIBytes(value, 64, 16);
this.buildInfo = this.bytes.ToASCIIString(64, 16);
}
}
/// <summary>
/// <para>80-89</para>
/// Set Device ID String, string is padded and terminated with '\0'
/// +1
/// </summary>
//public string DeviceID
//{
// set => this.SetASCIIBytes(value, 80, 10);
//}
private void SetASCIIBytes(string value, int start, int length)
{
if (string.IsNullOrWhiteSpace(value))
{
return;
}
var asciiBytes = Encoding.ASCII.GetBytes(value);
var asciiLength = asciiBytes.Length;
var asciiIndex = 0;
length += start;
while (start >= 0 && start < length && asciiIndex < asciiLength)
{
this.bytes[start++] = asciiBytes[asciiIndex++];
}
}
}
}
+5
View File
@@ -15,5 +15,10 @@
public CustomerText(byte[] response) : base(response, LENGTH)
{
}
public CustomerText(string value) : base(value, LENGTH)
{
}
}
}
+5
View File
@@ -11,5 +11,10 @@
public DeviceID(byte[] response) : base(response, LENGTH)
{
}
public DeviceID(string value) : base(value, LENGTH)
{
}
}
}
@@ -27,5 +27,7 @@
get => epoch.AddSeconds(this.SecondsSince);
set => this.SecondsSince = (uint)(value - epoch).TotalSeconds;
}
public static uint UTCNow => (uint)(DateTime.UtcNow - epoch).TotalSeconds;
}
}
+8
View File
@@ -12,5 +12,13 @@
public FactoryID(byte[] response) : base(response, LENGTH)
{
}
public FactoryID(string value) : base(value, LENGTH)
{
}
public static implicit operator FactoryID(string value)
=> new FactoryID { Value = value };
}
}
+6 -1
View File
@@ -1,4 +1,6 @@
namespace OmniPlus.Features
using System;
namespace OmniPlus.Features
{
public class FeatureRequest : FeatureResponse
{
@@ -25,5 +27,8 @@
return request;
}
}
internal void CopyTo(byte[] bytes, int start)
=> Array.Copy(this.bytes, 0, bytes, start, this.length);
}
}
+16 -7
View File
@@ -13,14 +13,14 @@ namespace OmniPlus.Features
public FeatureResponse(byte[] response, int length = 0) : this(length)
{
if (response != null )
if (response?.Length > 0)
{
this.Status = (Status)response[0];
}
if (response.Length > 1 && response.Length >= length)
{
Array.Copy(response, 1, this.bytes, 0, length);
}
if (response?.Length > length)
{
Array.Copy(response, 1, this.bytes, 0, length);
}
}
@@ -28,7 +28,16 @@ namespace OmniPlus.Features
public bool Succeeded => this.Status == Status.Completed;
public static implicit operator byte[](FeatureResponse response)
=> response?.bytes ?? new byte[0];
public override bool Equals(object obj)
=> obj is FeatureResponse outer && outer.bytes == this.bytes;
public override int GetHashCode()
=> this.bytes.GetHashCode();
public override string ToString()
=> BitConverter.ToString(this.bytes);
//public static implicit operator byte[](FeatureResponse response)
// => response?.bytes ?? new byte[0];
}
}
@@ -16,5 +16,13 @@
public OptionalUniDirFields(byte[] response) : base(response, LENGTH)
{
}
public OptionalUniDirFields(string value) : base(value, LENGTH)
{
}
public static implicit operator ushort(OptionalUniDirFields obj)
=> BitConverter.ToUInt16(obj.bytes, 0);
}
}
+11 -1
View File
@@ -1,4 +1,6 @@
namespace OmniPlus.Features
using System;
namespace OmniPlus.Features
{
public class ProgrammableID : ASCIIString
{
@@ -12,5 +14,13 @@
public ProgrammableID(byte[] response) : base(response, LENGTH)
{
}
public ProgrammableID(string value) : base(value, LENGTH)
{
}
public static implicit operator ProgrammableID(string value)
=> new ProgrammableID { Value = value };
}
}
+17 -1
View File
@@ -1,4 +1,6 @@
namespace OmniPlus.Features
using System;
namespace OmniPlus.Features
{
public class ReadingDigits : FeatureRequest
{
@@ -23,5 +25,19 @@
get => this.bytes[1];
set => this.bytes[1] = value;
}
public static implicit operator ReadingDigits(sbyte value)
{
var bytes = BitConverter.GetBytes(value);
return new ReadingDigits
{
Count = bytes[0],
Offset = bytes[1]
};
}
public static implicit operator sbyte(ReadingDigits readingDigits)
=> (sbyte)BitConverter.ToInt16(new[] { readingDigits?.Count ?? 0, readingDigits?.Offset ?? 0 }, 0);
}
}
+9 -1
View File
@@ -33,7 +33,7 @@ namespace OmniPlus.Features
/// </summary>
public class Readings : ASCIIString
{
const int LENGTH = 8;
const int LENGTH = 12;
public Readings() : base(LENGTH)
{
@@ -43,10 +43,18 @@ namespace OmniPlus.Features
{
}
public Readings(string value) : base(value, LENGTH)
{
}
public byte SensorID => this.bytes[0];
public ReadingSettings Setting => (ReadingSettings)this.bytes[1];
public bool Include => this.bytes[2] == 1;
public static implicit operator Readings(string value)
=> new Readings { Value = value };
}
}
@@ -91,21 +91,5 @@ namespace OmniPlus.Features
get => this.bytes[12].GetUint8(3, 3);
set => OmniPlusExtensions.SetUint8(ref this.bytes[12], 3, 3, value);
}
public override bool Equals(object other)
=> other is RegisterSettings registerSettings
&& registerSettings.IntPart == this.IntPart
&& registerSettings.Fraction == this.Fraction
&& registerSettings.Size == this.Size
&& registerSettings.Range == this.Range
&& registerSettings.VolumePerPulseUnit == this.VolumePerPulseUnit
&& registerSettings.MeterUnits == this.MeterUnits
&& registerSettings.FlowUnits == this.FlowUnits
&& registerSettings.FlowRate == this.FlowRate
&& registerSettings.FactoryCorrection == this.FactoryCorrection
&& registerSettings.FieldCorrection == this.FieldCorrection
&& registerSettings.PulseWeight == this.PulseWeight
&& registerSettings.PulseWidth == this.PulseWidth
&& registerSettings.PulseUpdate == this.PulseUpdate;
}
}
+2 -2
View File
@@ -19,7 +19,7 @@
{
this.BuildInfo = new BuildInfo();
this.BuildInfo.Status = this.Status;
this.BuildInfo.Value = this.bytes.ToASCIIString(0, 15);
this.BuildInfo.ToASCIIString(this.bytes, 0, 15);
this.ManifacturingTime = new ManifacturingTime();
this.ManifacturingTime.Status = this.Status;
@@ -33,7 +33,7 @@
this.VersionAndType = new VersionAndType();
this.VersionAndType.Status = this.Status;
this.VersionAndType.Value = this.bytes.ToASCIIString(25, 51);
this.VersionAndType.ToASCIIString(this.bytes, 25, 51);
this.BatteryVoltage = new BatteryVoltage();
this.BatteryVoltage.Status = this.Status;
@@ -21,5 +21,10 @@
public VersionAndType(byte[] response) : base(response, LENGTH)
{
}
public VersionAndType(string value) : base(value, LENGTH)
{
}
}
}
+4 -3
View File
@@ -112,10 +112,11 @@
<None Include="app.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SignalRClient\SignalRClient.csproj">
<Project>{5C16DAC4-71E5-49B4-A087-36C704AADCF1}</Project>
<Name>SignalRClient</Name>
<ProjectReference Include="..\ExchangeHub\ExchangeHub.csproj">
<Project>{6033FF0C-CF32-4BC5-8EFE-4774CCDC663D}</Project>
<Name>ExchangeHub</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
+20 -4
View File
@@ -1,12 +1,15 @@
using OmniPlus.Features;
using System;
using OmniPlus.Features;
using OmniPlus.Hanlers;
using static OmniPlus.Features.Features;
namespace OmniPlus
{
public class OmniPlusConnection
public sealed class OmniPlusConnection
{
private uint secondsToWait;
private Func<bool> connected;
private readonly IrdaConnection irdaConnection;
private readonly UniProConnection uniProConnection;
@@ -18,11 +21,15 @@ namespace OmniPlus
internal MessageHandler MessageHandler;
public bool Connected
=> this.connected?.Invoke() == true;
public bool ConnectIrda()
{
this.uniProConnection.Close();
this.irdaConnection.Open();
this.connected = () => this.irdaConnection.Connected;
this.MessageHandler = this.irdaConnection.SendMessage;
return this.irdaConnection.Connected;
@@ -33,6 +40,7 @@ namespace OmniPlus
this.irdaConnection.Close();
this.uniProConnection.Open();
this.connected = () => this.uniProConnection.Connected;
this.MessageHandler = this.uniProConnection.SendMessage;
return this.irdaConnection.Connected;
@@ -137,7 +145,7 @@ namespace OmniPlus
public ConfigurationParameters ViewConfigurationParameters()
{
var response = this.MessageHandler.Invoke(DEVICE_SPECIFIC, VIEW_CONFIGURATION_PARAMETERS, 0x01);
var response = this.MessageHandler.Invoke(DEVICE_SPECIFIC, VIEW_CONFIGURATION_PARAMETERS/*, 0x01*/);
return new ConfigurationParameters(response);
}
@@ -245,7 +253,7 @@ namespace OmniPlus
return new FeatureResponse(response);
}
public FeatureResponse Set(ConfigurationParameters configurationParameters)
public FeatureResponse Set(ConfigurationParametersSet configurationParameters)
{
var response = this.MessageHandler.Invoke(configurationParameters[DEVICE_SPECIFIC, SET_CONFIGURATION_PARAMETERS]);
@@ -383,5 +391,13 @@ namespace OmniPlus
return new FeatureResponse();
}
private void WaitBeforeNextExecution()
{
while (this.secondsToWait > 0)
{
this.secondsToWait--;
}
}
}
}
+85 -54
View File
@@ -1,5 +1,4 @@
using OmniPlus;
using OmniPlus.Enums;
using OmniPlus.Enums;
using OmniPlus.Features;
using System;
@@ -12,8 +11,9 @@ using static OmniPlus.OmniPlusConstants;
namespace OmniPlus
{
public class OmniPlusConnectionsPool
public class OmniPlusConnectionsPool : IDisposable
{
public event Action Done;
public event OmniPlusStateChanged StateChanged;
private readonly ConcurrentDictionary<int, OmniPlusConnection> connections;
@@ -45,6 +45,8 @@ namespace OmniPlus
public void Dispose()
{
this.signalRConnection.Dispose();
// Disposes all OmniPlus connections.
foreach (var connectionKVP in this.connections)
{
@@ -117,13 +119,30 @@ namespace OmniPlus
public void StartInspection()
{
this.background = new BackgroundWorker();
foreach (var position in this.connections.Keys)
{
//this.StartOmniInitialization(position);
this.background.DoWork += this.StartInspection;
//foreach (var point in this.omniType.TestPoints)
//{
// this.StartOmniInspection(position, point);
//}
this.background.RunWorkerAsync();
//this.CalculateCorrectionFactor(position);
this.FinishOmniInspection(position);
}
}
//public void StartInspection()
//{
// this.background = new BackgroundWorker();
// this.background.DoWork += this.StartInspection;
// this.background.RunWorkerAsync();
//}
private void StartInspection(object sender, EventArgs args)
{
var connectionsCount = this.connections.Count;
@@ -131,7 +150,7 @@ namespace OmniPlus
foreach (var position in this.connections.Keys)
{
tasks[position - 1] = Task.Factory.StartNew(() => this.StartOmniInitialization(position));
tasks[position - 1] = Task.Factory.StartNew(() => this.StartOmniInitialization(position), this.cancellationToken);
}
tasks.ContinueWhenAll();
@@ -139,38 +158,46 @@ namespace OmniPlus
foreach (var point in this.omniType.TestPoints)
{
// TODO: 600 = 10min * 60sec - change for production.
this.signalRConnection.SetQAndWaitUntilReached(point.VolumeM3H, 600);
if (!this.signalRConnection.SetQAndWaitUntilReached(point.VolumeM3H))
{
this.CancelExecution();
break;
}
tasks = new Task[connectionsCount];
foreach (var position in this.connections.Keys)
{
tasks[position - 1] = Task.Factory.StartNew(() => this.StartOmniInspection(position, point));
tasks[position - 1] = Task.Factory.StartNew(() => this.StartOmniInspection(position, point), this.cancellationToken);
}
tasks.ContinueWhenAll();
var volumeMessured = 0.1D; // TODO: this.GetM3();
var volume = this.signalRConnection.RequestRefV();
foreach (var position in this.connections.Keys)
{
this.SetPointDifference(position, point, volumeMessured);
this.SetPointDifference(position, point, volume);
}
}
foreach (var position in this.connections.Keys)
{
this.GetCorrectionFactor(position);
this.CalculateCorrectionFactor(position);
}
tasks = new Task[connectionsCount];
foreach (var position in this.connections.Keys)
{
tasks[position - 1] = Task.Factory.StartNew(() => this.FinishOmniInspection(position));
tasks[position - 1] = Task.Factory.StartNew(() => this.FinishOmniInspection(position), this.cancellationToken);
}
tasks.ContinueWhenAll();
this.Done?.Invoke();
}
private void StartOmniInitialization(int position)
@@ -204,7 +231,7 @@ namespace OmniPlus
{
var header = $"{point.VolumeM3H:0.0000}m³/h";
if (this.TryGetOmni(position, out var connection, out var meter) && meter.Initialized && meter.CanCalibrate)
if (this.TryGetOmni(position, out var connection, out var meter) /*&& meter.Initialized && meter.CanCalibrate*/)
{
this.StateChanged?.Invoke(position, 0, header, TBD);
@@ -231,11 +258,14 @@ namespace OmniPlus
if (!calibrationResult.Succeeded)
{
this.StateChanged?.Invoke(position, -1, header, $"CC: {calibrationResult.Status}");
this.StateChanged?.Invoke(position, -1, header, $"CC: {calibrationResult.Status.ToString()}");
}
else if (calibrationResult.Control != ChamberCalibrationResponse.TEST_COMPLETED)
{
this.StateChanged?.Invoke(position, -1, header, $"CC: {calibrationResult.Control}");
var control = calibrationResult.Control == ChamberCalibrationResponse.TEST_ACTIVATED
? nameof(ChamberCalibrationResponse.TEST_ACTIVATED)
: nameof(ChamberCalibrationResponse.TEST_FAILED);
this.StateChanged?.Invoke(position, -1, header, $"CC: {control}");
}
else // process calibration
{
@@ -271,7 +301,7 @@ namespace OmniPlus
}
}
private void GetCorrectionFactor(int position)
private void CalculateCorrectionFactor(int position)
{
if (this.TryGetOmni(position, out var _, out var meter) && meter.Initialized && meter.CanCalibrate)
{
@@ -287,7 +317,7 @@ namespace OmniPlus
private void FinishOmniInspection(int position)
{
if (this.TryGetOmni(position, out var connection, out var meter) && meter.Calibrated)
if (this.TryGetOmni(position, out var connection, out var meter)/* && meter.Calibrated*/)
{
var configurationParameters = connection.ViewConfigurationParameters();
var canContinue = configurationParameters.Succeeded;
@@ -305,49 +335,50 @@ namespace OmniPlus
//canContinue = systemTimeReset.Succeeded;
}
if (canContinue)
{
var registerSettings = connection.ViewRegisterSettings();
//if (canContinue)
//{
// var registerSettings = new RegisterSettings();
registerSettings.IntPart = this.omniType.IntPart;
registerSettings.Fraction = this.omniType.Fraction;
registerSettings.Size = this.omniType.Size;
registerSettings.Range = this.omniType.Range;
registerSettings.VolumePerPulseUnit = 0x00;
registerSettings.MeterUnits = (byte)MessurementUnits.USGallons;
registerSettings.FlowUnits = (byte)MessurementUnits.USGallons;
registerSettings.FlowRate = (byte)TimeUnits.Minutes;
registerSettings.FactoryCorrection = meter.FactoryCorrection;
registerSettings.FieldCorrection = 0x00;
registerSettings.PulseWeight = 0x01;
registerSettings.PulseWidth = 0x02;
registerSettings.PulseUpdate = 0x00;
// registerSettings.IntPart = this.omniType.IntPart;
// registerSettings.Fraction = this.omniType.Fraction;
// registerSettings.Size = this.omniType.Size;
// registerSettings.Range = this.omniType.Range;
// registerSettings.VolumePerPulseUnit = 0x00;
// registerSettings.MeterUnits = (byte)MessurementUnits.USGallons;
// registerSettings.FlowUnits = (byte)MessurementUnits.USGallons;
// registerSettings.FlowRate = (byte)TimeUnits.Minutes;
// registerSettings.FactoryCorrection = meter.FactoryCorrection;
// registerSettings.FieldCorrection = 0x00;
// registerSettings.PulseWeight = 0x01;
// registerSettings.PulseWidth = 0x02;
// registerSettings.PulseUpdate = 0x00;
var response = connection.Set(registerSettings);
canContinue = response.Succeeded;
// var response = connection.Set(registerSettings);
// canContinue = response.Succeeded;
if (canContinue)
{
meter.AddRegisterSettings(registerSettings);
}
}
// if (canContinue)
// {
// meter.AddRegisterSettings(registerSettings);
// }
//}
if (canContinue)
{
configurationParameters.ProgrammableID = meter.ProgrammableID;
configurationParameters.FactoryID = meter.FactoryID;
configurationParameters.ReadingDigits = meter.ReadingDigits;
configurationParameters.Readings = meter.Readings;
configurationParameters.OptionalUniDirFields = meter.OptionalUniDirFields;
configurationParameters.AlartPersistenceDays = meter.AlartPersistenceDays;
configurationParameters.DataLogIntervalMins = meter.DataLogIntervalMins;
configurationParameters.ManifacturingTime = meter.ManifacturingTime;
configurationParameters.SystemTime = DateTime.UtcNow;
configurationParameters.RebootCount = 0;
configurationParameters.BuildInfo = meter.BuildInfo;
configurationParameters.DeviceID = meter.DeviceID;
var configurationParametersSet = new ConfigurationParametersSet();
configurationParametersSet.ProgrammableID = "816232411982";
configurationParametersSet.FactoryID = "816232411982";
configurationParametersSet.NumberOfReadingDigits = 8; // meter.ReadingDigits;
configurationParametersSet.ReadingPreset = "0000000000";
configurationParametersSet.OptionalUniDirFields = 0; // meter.OptionalUniDirFields;
configurationParametersSet.AlarmPercistancePeriodDays = 31;
configurationParametersSet.DataLogIntervalMinutes = 60;
configurationParametersSet.ManifacturingTime = Epoch20000101.UTCNow - 30000; // meter.ManifacturingTime;
configurationParametersSet.SystemTime = Epoch20000101.UTCNow;
configurationParametersSet.RebootCount = 0;
configurationParametersSet.BuildInfo = "816232411982"; // meter.BuildInfo;
// configurationParametersSet.DeviceID = "OMNI-01";
var response = connection.Set(configurationParameters);
var response = connection.Set(configurationParametersSet);
canContinue = response.Succeeded;
}
+15 -9
View File
@@ -1,5 +1,5 @@
using OmniPlus.Features;
using System;
using System.Data.SqlClient;
namespace OmniPlus
@@ -75,9 +75,8 @@ namespace OmniPlus
, [ReadingDigitsOffsest] = @{nameof(parameters.ReadingDigits.Offset)}
, [Readings] = @{nameof(parameters.Readings)}
, [OptionalUniDirFields] = @{nameof(parameters.OptionalUniDirFields)}
, [AlarmPersitenceDays] = @{nameof(parameters.AlartPersistenceDays)}
, [AlarmPersitenceDays] = @{nameof(parameters.AlarmPersistenceDays)}
, [DataLogIntervalMins] = @{nameof(parameters.DataLogIntervalMins)}
, [DeviceID] = @{nameof(parameters.DeviceID)}
WHERE [Position] = @{nameof(position)}";
sqlCommand.Parameters.Add(new SqlParameter(nameof(position), position));
sqlCommand.Parameters.Add(new SqlParameter(nameof(parameters.ProgrammableID), parameters.ProgrammableID.Value));
@@ -85,12 +84,19 @@ namespace OmniPlus
sqlCommand.Parameters.Add(new SqlParameter(nameof(parameters.ReadingDigits.Count), parameters.ReadingDigits.Count));
sqlCommand.Parameters.Add(new SqlParameter(nameof(parameters.ReadingDigits.Offset), parameters.ReadingDigits.Offset));
sqlCommand.Parameters.Add(new SqlParameter(nameof(parameters.Readings), parameters.Readings.Value));
sqlCommand.Parameters.Add(new SqlParameter(nameof(parameters.OptionalUniDirFields), parameters.OptionalUniDirFields));
sqlCommand.Parameters.Add(new SqlParameter(nameof(parameters.AlartPersistenceDays), parameters.AlartPersistenceDays));
sqlCommand.Parameters.Add(new SqlParameter(nameof(parameters.DataLogIntervalMins), parameters.DataLogIntervalMins));
sqlCommand.Parameters.Add(new SqlParameter(nameof(parameters.DeviceID), parameters.DeviceID.Value));
sqlCommand.ExecuteNonQuery();
sqlCommand.Parameters.Clear();
sqlCommand.Parameters.Add(new SqlParameter(nameof(parameters.OptionalUniDirFields), parameters.OptionalUniDirFields.Value));
sqlCommand.Parameters.Add(new SqlParameter(nameof(parameters.AlarmPersistenceDays), parameters.AlarmPersistenceDays));
sqlCommand.Parameters.Add(new SqlParameter(nameof(parameters.DataLogIntervalMins), BitConverter.GetBytes(parameters.DataLogIntervalMins).Reverse()));
try
{
sqlCommand.ExecuteNonQuery();
sqlCommand.Parameters.Clear();
}
catch (System.Exception e)
{
}
}
}
}
+5 -6
View File
@@ -56,7 +56,7 @@ namespace OmniPlus
control.ReturnResponse = true;
message[index++] = control;
bytes.CopyTo(message, index--);
bytes.CopyTo(message, index);
var checksum = message.CalculateCheckSUM(message.Length - OxO2);
var checksumIndex = bytesLength + UI1236_HED_LENGTH;
@@ -264,17 +264,16 @@ namespace OmniPlus
}
}
public static string ToASCIIString(this byte[] bytes, int start, int end)
public static string ToASCIIString(this byte[] bytes, int start, int length)
{
var asciiString = string.Empty;
while (start <= end)
while (start >= 0 && start < bytes.Length)
{
asciiString += (char)bytes[start];
start++;
asciiString += (char)bytes[start++];
}
return asciiString.Trim();
return asciiString;
}
public static void ContinueWhenAll(this Task[] tasks)
+29 -18
View File
@@ -18,6 +18,7 @@ namespace OmniPlus
{
this.Connected = connected;
this.systemParameters = new SystemParameters();
this.configurationParameters = new ConfigurationParameters();
this.testPoints = new Dictionary<OmniPlusTestPoint, OmniPlusTest>();
}
@@ -37,22 +38,22 @@ namespace OmniPlus
public string BuildInfo
{
get => this.systemParameters.BuildInfo.Value.Trim('\0');
get => this.systemParameters.BuildInfo.Value;
set => this.systemParameters.BuildInfo.Value = value;
}
public DateTime ManifacturingTime
public uint ManifacturingTime
{
get => this.systemParameters.ManifacturingTime.DateTime;
set => this.systemParameters.ManifacturingTime.DateTime = value;
get => this.systemParameters.ManifacturingTime.SecondsSince;
set => this.systemParameters.ManifacturingTime.SecondsSince = value;
}
public bool ManifacturingTimeInRange { get; private set; }
public DateTime SystemTime
public uint SystemTime
{
get => this.systemParameters.SystemTime.DateTime;
set => this.systemParameters.SystemTime.DateTime = value;
get => this.systemParameters.SystemTime.SecondsSince;
set => this.systemParameters.SystemTime.SecondsSince = value;
}
public int SystemTimeDrift { get; private set; }
@@ -67,7 +68,7 @@ namespace OmniPlus
public string Firmware
{
get => this.systemParameters.VersionAndType.Value.Trim('\0');
get => this.systemParameters.VersionAndType.Value;
set => this.systemParameters.VersionAndType.Value = value;
}
@@ -100,8 +101,8 @@ namespace OmniPlus
public FactoryID FactoryID
=> this.configurationParameters.FactoryID;
public ReadingDigits ReadingDigits
=> this.configurationParameters.ReadingDigits;
public sbyte ReadingDigits
=> this.configurationParameters?.ReadingDigits ?? 0;
public Readings Readings
=> this.configurationParameters.Readings;
@@ -110,14 +111,11 @@ namespace OmniPlus
=> this.configurationParameters.OptionalUniDirFields;
public byte AlartPersistenceDays
=> this.configurationParameters.AlartPersistenceDays;
=> this.configurationParameters.AlarmPersistenceDays;
public ushort DataLogIntervalMins
=> this.configurationParameters.DataLogIntervalMins;
public DeviceID DeviceID
=> this.configurationParameters.DeviceID;
internal double AddCalibrationResult(OmniPlusTestPoint point, ChamberCalibrationResponse result)
{
this.testPoints[point] = new OmniPlusTest
@@ -149,16 +147,29 @@ namespace OmniPlus
{
this.systemParameters = systemParameters;
this.SystemTimeDrift = (DateTime.UtcNow - this.SystemTime).Seconds;
this.SystemTimeDrift = (DateTime.UtcNow - this.systemParameters.SystemTime.DateTime).Seconds;
this.SystemTimeInRange = Math.Abs(this.SystemTimeDrift) <= 700;
this.ManifacturingTimeInRange = Math.Abs((DateTime.UtcNow - this.ManifacturingTime).Days) <= 365;
this.ManifacturingTimeInRange = Math.Abs((DateTime.UtcNow - this.systemParameters.ManifacturingTime.DateTime).Days) <= 365;
var firmwareTokens = this.Firmware
?.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)
?? new string[2];
var versionString = firmwareTokens[0]?.Substring(1) ?? string.Empty;
var versionString = string.Empty;
if (firmwareTokens?.Length > 0 && firmwareTokens[0]?.Length > 0)
{
versionString = firmwareTokens[0].Substring(1);
}
var versionParsed = double.TryParse(versionString, out var version);
var typeString = firmwareTokens[1]?.ToUpper() ?? string.Empty;
var typeString = string.Empty;
if (firmwareTokens?.Length > 1 && firmwareTokens[1]?.Length > 0)
{
typeString = firmwareTokens[1].ToUpper();
}
var isOmni = typeString == "OMNI-01" || typeString == "OMNI-02";
this.FirmwareInRange = versionParsed && version >= 1.23 && isOmni;
+66 -14
View File
@@ -1,39 +1,91 @@
namespace OmniPlus
{
using SignalRClient;
using System.Threading;
using ExchangeHub;
public class SignalRConnection
using System;
using System.Threading;
namespace OmniPlus
{
public class SignalRConnection : IDisposable
{
private readonly ExchangeHub exchangeHub;
private readonly ExchangeConnection slave;
private readonly string url;
public SignalRConnection(string url)
{
this.url = url;
this.exchangeHub = new ExchangeHub();
this.slave = new ExchangeConnection();
}
internal void Connect()
=> this.exchangeHub.Connect(this.url);
=> this.slave.Connect(this.url);
public bool SetQAndWaitUntilReached(double value, int seconds)
internal bool SetQAndWaitUntilReached(double value, int seconds = 300)
{
var reachSeconds = seconds;
var reached = false;
var started = false;
void WaitUntilReached(double _) => reached = true;
this.exchangeHub.ExchangeQ += WaitUntilReached;
void waitQReached(double _) => reached = true;
this.slave.StartQ += waitQReached;
this.exchangeHub.SendExchangeQ(value);
this.slave.NotifyStartQ(value);
while (!reached && seconds >= 0)
while (!reached && reachSeconds >= 0)
{
Thread.Sleep(1000);
reachSeconds--;
}
if (reached)
{
var startSeconds = seconds;
void waitRefStarted() => started = true;
this.slave.StartRef += waitRefStarted;
this.slave.NotifyStartRef();
while (!started && startSeconds >= 0)
{
Thread.Sleep(1000);
startSeconds--;
}
}
return reached && started;
}
internal void StopRunning()
=> this.slave.NotifyStopQ();
internal double RequestRefV(int seconds = 300)
{
var received = false;
var referenceV = 0D;
void waitRefV(double value)
{
referenceV = value;
received = true;
}
this.slave.StopRef += waitRefV;
this.slave.NotifyStopRef();
while (!received && seconds >= 0)
{
Thread.Sleep(1000);
seconds--;
}
return reached;
return referenceV;
}
public void Dispose()
=> this.slave.Dispose();
}
}