omni progress
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
using OmniPlus.Enums;
|
||||
using OmniPlus.Hanlers;
|
||||
|
||||
using System;
|
||||
using System.IO.Ports;
|
||||
using System.Threading;
|
||||
|
||||
namespace OmniPlus.Connections
|
||||
{
|
||||
internal class IrdaConnection : SerialPortConnection
|
||||
{
|
||||
protected event DataReceivedHandler MessageReceived;
|
||||
|
||||
public IrdaConnection(string portName)
|
||||
{
|
||||
this.Options.PortName = portName;
|
||||
this.Options.BaudRate = BaudRate.K115200;
|
||||
this.Options.DataBits = DataBits.X8;
|
||||
this.Options.Parity = Parity.None;
|
||||
this.Options.StopBits = StopBits.One;
|
||||
this.Options.LogFile = $".\\IRDA\\[{portName}].txt";
|
||||
}
|
||||
|
||||
public byte[] SendMessage(byte[] command)
|
||||
{
|
||||
var response = new byte[0];
|
||||
var received = false;
|
||||
|
||||
if (command is null)
|
||||
{
|
||||
return response;
|
||||
}
|
||||
|
||||
void MessageReceivedHandler(byte[] bytes)
|
||||
{
|
||||
response = bytes
|
||||
.FromIrdaMessage()
|
||||
.FromUI1236Message();
|
||||
received = true;
|
||||
}
|
||||
|
||||
this.MessageReceived += MessageReceivedHandler;
|
||||
|
||||
this.Write(1, 1, 1, 1);
|
||||
|
||||
var request = command
|
||||
.ToUI1236Message()
|
||||
.ToIrdaMessage();
|
||||
var sleep = 5000;
|
||||
var delay = 500;
|
||||
|
||||
this.Write(request);
|
||||
|
||||
while (!received && sleep >= 0)
|
||||
{
|
||||
sleep -= delay;
|
||||
|
||||
Thread.Sleep(delay);
|
||||
}
|
||||
|
||||
this.MessageReceived -= MessageReceivedHandler;
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
protected override void SerialPortConnection_DataReceived(byte[] bytes)
|
||||
{
|
||||
var message = default(byte[]);
|
||||
var length = bytes.Length;
|
||||
var start = 0;
|
||||
|
||||
while (start < bytes.Length)
|
||||
{
|
||||
if (bytes[start] == OmniPlusExtensions.IRDA_START)
|
||||
{
|
||||
try
|
||||
{
|
||||
var messageLength = bytes[start + 2] * 2;
|
||||
messageLength += OmniPlusExtensions.IRDA_MIN_LENGTH;
|
||||
message = new byte[messageLength];
|
||||
|
||||
Array.Copy(bytes, start, message, 0, messageLength);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.SerialPortConnection_LogMessage(e.ToString());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
start++;
|
||||
}
|
||||
|
||||
this.MessageReceived?.Invoke(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
using OmniPlus.Enums;
|
||||
using OmniPlus.Hanlers;
|
||||
using OmniPlus.Options;
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Ports;
|
||||
|
||||
namespace OmniPlus.Connections
|
||||
{
|
||||
internal abstract class SerialPortConnection
|
||||
{
|
||||
protected event StateChangedEventHandler StateChanged;
|
||||
protected event DataReceivedHandler DataReceived;
|
||||
protected event LogMessageEventHandler LogMessage;
|
||||
|
||||
private SerialPort serialPort;
|
||||
private SerialPortState state;
|
||||
|
||||
protected SerialPortConnection()
|
||||
=> this.Options = new SerialPortOptions();
|
||||
|
||||
protected SerialPortOptions Options { get; }
|
||||
|
||||
public bool Connected => this.state == SerialPortState.Open;
|
||||
|
||||
public virtual void Close()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (this.serialPort != null)
|
||||
{
|
||||
this.serialPort.DataReceived -= this.SerialPort_DataReceived;
|
||||
this.serialPort.Disposed -= this.SerialPort_Disposed;
|
||||
this.serialPort.ErrorReceived -= this.SerialPort_ErrorReceived;
|
||||
this.serialPort.PinChanged -= this.SerialPort_PinChanged;
|
||||
}
|
||||
|
||||
this.serialPort?.Close();
|
||||
this.serialPort?.Dispose();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.StateChanged?.Invoke(SerialPortState.ErrorReceived, e.ToString());
|
||||
}
|
||||
|
||||
this.serialPort = null;
|
||||
this.state = SerialPortState.Closed;
|
||||
|
||||
this.StateChanged?.Invoke(this.state, $"{this.Options.PortName} closed.");
|
||||
|
||||
if (this.LogMessage != null)
|
||||
{
|
||||
foreach (var @delegate in this.LogMessage.GetInvocationList())
|
||||
{
|
||||
if (@delegate is LogMessageEventHandler eventHandler)
|
||||
{
|
||||
this.LogMessage -= eventHandler;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.StateChanged != null)
|
||||
{
|
||||
foreach (var @delegate in this.StateChanged.GetInvocationList())
|
||||
{
|
||||
if (@delegate is LogMessageEventHandler eventHandler)
|
||||
{
|
||||
this.LogMessage -= eventHandler;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.DataReceived != null)
|
||||
{
|
||||
foreach (var @delegate in this.DataReceived.GetInvocationList())
|
||||
{
|
||||
if (@delegate is LogMessageEventHandler eventHandler)
|
||||
{
|
||||
this.LogMessage -= eventHandler;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Open()
|
||||
{
|
||||
this.Close();
|
||||
|
||||
try
|
||||
{
|
||||
var fileInfo = new FileInfo(this.Options.LogFile);
|
||||
fileInfo.Directory.Create();
|
||||
|
||||
File.AppendAllText(this.Options.LogFile, string.Empty);
|
||||
|
||||
this.LogMessage += this.SerialPortConnection_LogMessage;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.state = SerialPortState.ErrorReceived;
|
||||
|
||||
this.StateChanged?.Invoke(this.state, $"{this.Options.PortName}: {e}");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
this.serialPort = new SerialPort
|
||||
{
|
||||
PortName = this.Options.PortName,
|
||||
BaudRate = (int)this.Options.BaudRate,
|
||||
DataBits = (int)this.Options.DataBits,
|
||||
Parity = this.Options.Parity,
|
||||
StopBits = this.Options.StopBits,
|
||||
RtsEnable = this.Options.RtsEnabled,
|
||||
DtrEnable = this.Options.DtrEnabled
|
||||
};
|
||||
|
||||
this.serialPort.DataReceived += this.SerialPort_DataReceived;
|
||||
this.serialPort.Disposed += this.SerialPort_Disposed;
|
||||
this.serialPort.ErrorReceived += this.SerialPort_ErrorReceived;
|
||||
this.serialPort.PinChanged += this.SerialPort_PinChanged;
|
||||
this.DataReceived += this.SerialPortConnection_DataReceived;
|
||||
this.StateChanged += this.SerialPortConnection_StateChanged;
|
||||
|
||||
this.serialPort.Open();
|
||||
|
||||
this.state = SerialPortState.Open;
|
||||
|
||||
this.StateChanged?.Invoke(this.state
|
||||
, $"{this.Options.PortName} opened"
|
||||
+ $", {this.Options.BaudRate}"
|
||||
+ $", {this.Options.DataBits}"
|
||||
+ $", {this.Options.Parity}"
|
||||
+ $", {this.Options.StopBits}");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.state = SerialPortState.ErrorReceived;
|
||||
|
||||
this.StateChanged?.Invoke(this.state, $"{this.Options.PortName}: {e}");
|
||||
|
||||
this.Close();
|
||||
}
|
||||
}
|
||||
|
||||
protected void Write(params byte[] message)
|
||||
{
|
||||
if (this.Connected && message.Length > 0)
|
||||
{
|
||||
this.serialPort.Write(message, 0, message.Length);
|
||||
}
|
||||
|
||||
this.LogMessage?.Invoke($"TX: {BitConverter.ToString(message)}");
|
||||
}
|
||||
|
||||
protected abstract void SerialPortConnection_DataReceived(byte[] bytes);
|
||||
|
||||
protected void SerialPortConnection_LogMessage(string message)
|
||||
=> File.AppendAllText(this.Options.LogFile, $"{message}{Environment.NewLine}");
|
||||
|
||||
protected void SerialPortConnection_StateChanged(SerialPortState state, string message)
|
||||
=> File.AppendAllText(this.Options.LogFile, $"[{state}] {message}{Environment.NewLine}");
|
||||
|
||||
private void SerialPort_PinChanged(object sender, SerialPinChangedEventArgs args)
|
||||
{
|
||||
this.state = SerialPortState.PinChanged;
|
||||
|
||||
this.StateChanged?.Invoke(this.state, $"{this.Options.PortName} {args.EventType}.");
|
||||
|
||||
this.Close();
|
||||
}
|
||||
|
||||
private void SerialPort_ErrorReceived(object sender, SerialErrorReceivedEventArgs args)
|
||||
{
|
||||
this.state = SerialPortState.ErrorReceived;
|
||||
|
||||
this.StateChanged?.Invoke(this.state, $"{this.Options.PortName} {args.EventType}.");
|
||||
|
||||
this.Close();
|
||||
}
|
||||
|
||||
private void SerialPort_Disposed(object sender, EventArgs args)
|
||||
{
|
||||
this.state = SerialPortState.Disposed;
|
||||
|
||||
this.StateChanged?.Invoke(this.state, $"{this.Options.PortName} disposed.");
|
||||
|
||||
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();
|
||||
|
||||
lock (lock_object)
|
||||
{
|
||||
port.Read(bytes, 0, port.BytesToRead);
|
||||
}
|
||||
|
||||
this.DataReceived?.Invoke(bytes);
|
||||
this.LogMessage?.Invoke($"RX: {BitConverter.ToString(bytes)}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
using OmniPlus.Enums;
|
||||
using OmniPlus.Hanlers;
|
||||
|
||||
using System;
|
||||
using System.IO.Ports;
|
||||
using System.Threading;
|
||||
|
||||
namespace OmniPlus.Connections
|
||||
{
|
||||
internal class UniProConnection : SerialPortConnection
|
||||
{
|
||||
protected event DataReceivedHandler MessageReceived;
|
||||
|
||||
public UniProConnection(string portName)
|
||||
{
|
||||
this.Options.PortName = portName;
|
||||
this.Options.BaudRate = BaudRate.K2400;
|
||||
this.Options.DataBits = DataBits.X8;
|
||||
this.Options.Parity = Parity.None;
|
||||
this.Options.StopBits = StopBits.One;
|
||||
this.Options.LogFile = $".\\UniPro\\[{portName}].txt";
|
||||
}
|
||||
|
||||
public byte[] SendMessage(byte[] command)
|
||||
{
|
||||
var response = new byte[0];
|
||||
var received = false;
|
||||
|
||||
if (command is null)
|
||||
{
|
||||
return response;
|
||||
}
|
||||
|
||||
void MessageReceivedHandler(byte[] bytes)
|
||||
{
|
||||
response = bytes.FromUniProMessage();
|
||||
received = true;
|
||||
}
|
||||
|
||||
this.MessageReceived += MessageReceivedHandler;
|
||||
|
||||
var request = command.ToUniProMessage();
|
||||
var sleep = 5000;
|
||||
var delay = 500;
|
||||
|
||||
this.Write(request);
|
||||
|
||||
while (!received && sleep >= 0)
|
||||
{
|
||||
sleep -= delay;
|
||||
|
||||
Thread.Sleep(delay);
|
||||
}
|
||||
|
||||
this.MessageReceived -= MessageReceivedHandler;
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
protected override void SerialPortConnection_DataReceived(byte[] bytes)
|
||||
{
|
||||
var message = default(byte[]);
|
||||
var length = bytes.Length;
|
||||
var start = 0;
|
||||
|
||||
while (start < bytes.Length)
|
||||
{
|
||||
if (bytes[start] == OmniPlusExtensions.UNIPRO_START)
|
||||
{
|
||||
try
|
||||
{
|
||||
var messageLength = bytes[start + 2];
|
||||
messageLength += OmniPlusExtensions.UNIPRO_MIN_LENGTH;
|
||||
message = new byte[messageLength];
|
||||
|
||||
Array.Copy(bytes, start, message, 0, messageLength);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.SerialPortConnection_LogMessage(e.ToString());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
start++;
|
||||
}
|
||||
|
||||
this.MessageReceived?.Invoke(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace OmniPlus.Enums
|
||||
{
|
||||
public enum BaudRate
|
||||
{
|
||||
K300 = 300,
|
||||
K600 = 600,
|
||||
K1200 = 1200,
|
||||
K2400 = 2400,
|
||||
K4800 = 4800,
|
||||
K9600 = 9600,
|
||||
K19200 = 19200,
|
||||
K38400 = 38400,
|
||||
K57600 = 57600,
|
||||
K115200 = 115200,
|
||||
K230400 = 230400,
|
||||
K460800 = 460800,
|
||||
K921600 = 921600
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace OmniPlus.Enums
|
||||
{
|
||||
public enum DataBits
|
||||
{
|
||||
X5 = 5,
|
||||
X6 = 6,
|
||||
X7 = 7,
|
||||
X8 = 8
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace OmniPlus.Enums
|
||||
{
|
||||
public enum MessurementUnits : byte
|
||||
{
|
||||
CubicMeters = 0,
|
||||
CubicFeet = 1,
|
||||
CubicInches = 2,
|
||||
CubicYards = 3,
|
||||
USGallons = 4,
|
||||
ImperialGallons = 5,
|
||||
AcreFeet = 6,
|
||||
Kiloliters = 7,
|
||||
Liters = 8,
|
||||
WattHours = 9,
|
||||
KilowattHours = 10,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace OmniPlus.Enums
|
||||
{
|
||||
public enum ReadingSettings : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// When forward and reverse volumes are supported,
|
||||
/// this will be equal to the value of the expression (total forward volume) – (total reverse volume).
|
||||
/// Use this value for sensors with no concept of forward or reverse.
|
||||
/// </summary>
|
||||
Totalizer = 0x00,
|
||||
TotalForward = 0x01,
|
||||
TotalReverse = 0x02,
|
||||
LifetimeTotalizer = 0x03,
|
||||
TestTotalizer = 0x04,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace OmniPlus.Enums
|
||||
{
|
||||
public enum SerialPortState
|
||||
{
|
||||
Closed,
|
||||
Disposed,
|
||||
PinChanged,
|
||||
ErrorReceived,
|
||||
Open
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
namespace OmniPlus.Enums
|
||||
{
|
||||
public enum Status : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Used by communication devices to indicate communication failure/timeout.
|
||||
/// Should not be used by a slave device implementing UI-1236.
|
||||
/// </summary>
|
||||
FailureOrTimeout = 0x00,
|
||||
/// <summary>
|
||||
/// Command complete, no errors
|
||||
/// </summary>
|
||||
Completed = 0x01,
|
||||
/// <summary>
|
||||
/// Error, unable to execute command
|
||||
/// </summary>
|
||||
UnableToExecute = 0x02,
|
||||
/// <summary>
|
||||
/// Error, unknown command
|
||||
/// </summary>
|
||||
UnknownCommand = 0x03,
|
||||
/// <summary>
|
||||
/// Error, unsupported control bit pattern
|
||||
/// </summary>
|
||||
UnsupportedControl = 0x04,
|
||||
/// <summary>
|
||||
/// Error, command blocked by factory seal.
|
||||
/// Added in version B1.23
|
||||
/// </summary>
|
||||
BlockedBySeal = 0x05,
|
||||
/// <summary>
|
||||
/// Custom field.
|
||||
/// </summary>
|
||||
Request = 0x80,
|
||||
/// <summary>
|
||||
/// Frame received, no error
|
||||
/// </summary>
|
||||
FrameReceived = 0xFC,
|
||||
/// <summary>
|
||||
/// Message corrupt
|
||||
/// </summary>
|
||||
MessageCorrupt = 0,
|
||||
/// <summary>
|
||||
/// Wait status
|
||||
/// </summary>
|
||||
Waiting = 0xFE,
|
||||
/// <summary>
|
||||
/// Used by communication devices to indicate unrecognized communication device command.
|
||||
/// </summary>
|
||||
UnrecognizedDevice = 0xFF
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace OmniPlus.Enums
|
||||
{
|
||||
public enum TimeUnits : byte
|
||||
{
|
||||
Milliseconds = 0,
|
||||
Seconds = 1,
|
||||
Minutes = 2,
|
||||
Hours = 3,
|
||||
Days = 4,
|
||||
Years = 5,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
namespace OmniPlus.Features
|
||||
{
|
||||
using System.Text;
|
||||
|
||||
public class ASCIIString : FeatureRequest
|
||||
{
|
||||
public ASCIIString(int length) : base(length)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public ASCIIString(byte[] response, int length) : base(response, length)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public string Value
|
||||
{
|
||||
get => this.GetValue();
|
||||
set => this.SetValue(value);
|
||||
}
|
||||
|
||||
private string GetValue()
|
||||
=> Encoding.ASCII
|
||||
.GetString(this.bytes)
|
||||
.Trim();
|
||||
|
||||
private void SetValue(string value)
|
||||
{
|
||||
this.bytes.Clear();
|
||||
|
||||
var factoryID = value
|
||||
?.Substring(0, this.length)
|
||||
?? string.Empty;
|
||||
|
||||
Encoding.ASCII
|
||||
.GetBytes(factoryID)
|
||||
.CopyTo(this.bytes, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
namespace OmniPlus.Features
|
||||
{
|
||||
using System;
|
||||
|
||||
public class BatteryVoltage : FeatureRequest
|
||||
{
|
||||
const int LENGTH = 6;
|
||||
|
||||
public BatteryVoltage() : base(LENGTH)
|
||||
{
|
||||
}
|
||||
|
||||
public BatteryVoltage(byte[] response) : base(response, LENGTH)
|
||||
{
|
||||
}
|
||||
|
||||
public ushort Volts
|
||||
{
|
||||
get => BitConverter.ToUInt16(this.bytes, 0);
|
||||
set => BitConverter.GetBytes(value).CopyTo(this.bytes, 0);
|
||||
}
|
||||
|
||||
public uint ADCCount
|
||||
{
|
||||
get => BitConverter.ToUInt32(this.bytes, 2);
|
||||
set => BitConverter.GetBytes(value).CopyTo(this.bytes, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace OmniPlus.Features
|
||||
{
|
||||
/// <summary>
|
||||
/// Build Information string must be at most 14 characters long. There are no restrictions on character type.
|
||||
/// Command is not available after factory seal.
|
||||
/// </summary>
|
||||
public class BuildInfo : ASCIIString
|
||||
{
|
||||
const int LENGTH = 14;
|
||||
|
||||
public BuildInfo() : base(LENGTH)
|
||||
{
|
||||
}
|
||||
|
||||
public BuildInfo(byte[] response) : base(response, LENGTH)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
namespace OmniPlus.Features
|
||||
{
|
||||
using System;
|
||||
|
||||
public class ConfigurationParameters : FeatureRequest
|
||||
{
|
||||
const int LENGTH = 62;
|
||||
|
||||
public ConfigurationParameters() : base(LENGTH)
|
||||
{
|
||||
}
|
||||
|
||||
public ConfigurationParameters(byte[] response) : base(response, LENGTH)
|
||||
{
|
||||
this.ProgrammableID = new ProgrammableID();
|
||||
this.ProgrammableID.Status = this.Status;
|
||||
this.ProgrammableID.Value = this.bytes.ToASCIIString(0, 20);
|
||||
|
||||
this.FactoryID = new FactoryID();
|
||||
this.FactoryID.Status = this.Status;
|
||||
this.FactoryID.Value = this.bytes.ToASCIIString(21, 33);
|
||||
|
||||
this.ReadingDigits = new ReadingDigits();
|
||||
this.ReadingDigits.Status = this.Status;
|
||||
this.ReadingDigits.Count = this.bytes[34];
|
||||
this.ReadingDigits.Offset = this.bytes[35];
|
||||
|
||||
this.Readings = new Readings();
|
||||
this.Readings.Status = this.Status;
|
||||
this.Readings.Value = this.bytes.ToASCIIString(36, 45);
|
||||
|
||||
this.OptionalUniDirFields = new OptionalUniDirFields();
|
||||
this.OptionalUniDirFields.Status = this.Status;
|
||||
this.OptionalUniDirFields.Past(this.bytes, 46);
|
||||
|
||||
this.AlartPersistencePeriod = this.bytes[50];
|
||||
this.DataLogIntervalMinutes = BitConverter.ToUInt16(this.bytes, 51);
|
||||
|
||||
this.DeviceID = new DeviceID();
|
||||
this.Readings.Status = this.Status;
|
||||
this.Readings.Value = this.bytes.ToASCIIString(53, 61);
|
||||
}
|
||||
|
||||
public ProgrammableID ProgrammableID { get; set; }
|
||||
|
||||
public FactoryID FactoryID { get; set; }
|
||||
|
||||
public ReadingDigits ReadingDigits { get; set; }
|
||||
|
||||
public Readings Readings { get; set; }
|
||||
|
||||
public OptionalUniDirFields OptionalUniDirFields { get; set; }
|
||||
|
||||
public byte AlartPersistencePeriod { get; set; }
|
||||
|
||||
public ushort DataLogIntervalMinutes { get; set; }
|
||||
|
||||
public DeviceID DeviceID { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace OmniPlus.Features
|
||||
{
|
||||
/// <summary>
|
||||
/// All printable characters except ';' (semicolon) and ',' (comma) are allowed.
|
||||
/// </summary>
|
||||
public class CustomerText : ASCIIString
|
||||
{
|
||||
const int LENGTH = 20;
|
||||
|
||||
public CustomerText() : base(LENGTH)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public CustomerText(byte[] response) : base(response, LENGTH)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace OmniPlus.Features
|
||||
{
|
||||
public class DeviceID : ASCIIString
|
||||
{
|
||||
const int LENGTH = 9;
|
||||
|
||||
public DeviceID() : base(LENGTH)
|
||||
{
|
||||
}
|
||||
|
||||
public DeviceID(byte[] response) : base(response, LENGTH)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
namespace OmniPlus.Features
|
||||
{
|
||||
using System;
|
||||
|
||||
public class Epoch20000101 : FeatureRequest
|
||||
{
|
||||
static readonly DateTime epoch = new DateTime(2000, 01, 01, 00, 00, 00, DateTimeKind.Utc);
|
||||
|
||||
const int LENGTH = 4;
|
||||
|
||||
public Epoch20000101() : base(LENGTH)
|
||||
{
|
||||
}
|
||||
|
||||
public Epoch20000101(byte[] response) : base(response, LENGTH)
|
||||
{
|
||||
}
|
||||
|
||||
public uint SecondsSince
|
||||
{
|
||||
get => BitConverter.ToUInt32(this.bytes, 0);
|
||||
set => BitConverter.GetBytes(value).CopyTo(this.bytes, 0);
|
||||
}
|
||||
|
||||
public DateTime DateTime
|
||||
{
|
||||
get => epoch.AddSeconds(this.SecondsSince);
|
||||
set => this.SecondsSince = (uint)(value - epoch).TotalSeconds;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace OmniPlus.Features
|
||||
{
|
||||
public class FactoryID : ASCIIString
|
||||
{
|
||||
const int LENGTH = 12;
|
||||
|
||||
public FactoryID() : base(LENGTH)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public FactoryID(byte[] response) : base(response, LENGTH)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
namespace OmniPlus.Features
|
||||
{
|
||||
public class FeatureRequest : FeatureResponse
|
||||
{
|
||||
protected readonly int length;
|
||||
|
||||
public FeatureRequest(int length)
|
||||
: base(length)
|
||||
=> this.length = length;
|
||||
|
||||
public FeatureRequest(byte[] response, int length = 0)
|
||||
: base(response, length)
|
||||
=> this.length = length;
|
||||
|
||||
public byte[] this[params byte[] cmd]
|
||||
{
|
||||
get
|
||||
{
|
||||
var cmdLength = cmd.Length;
|
||||
var request = new byte[this.length + cmdLength];
|
||||
|
||||
cmd.CopyTo(request, 0);
|
||||
this.bytes.CopyTo(request, cmdLength);
|
||||
|
||||
return request;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using OmniPlus.Enums;
|
||||
|
||||
using System;
|
||||
|
||||
namespace OmniPlus.Features
|
||||
{
|
||||
public class FeatureResponse
|
||||
{
|
||||
protected readonly byte[] bytes;
|
||||
|
||||
public FeatureResponse(int length = 0)
|
||||
=> this.bytes = new byte[length];
|
||||
|
||||
public FeatureResponse(byte[] response, int length = 0) : this(length)
|
||||
{
|
||||
if (response != null )
|
||||
{
|
||||
this.Status = (Status)response[0];
|
||||
|
||||
if (response.Length > 1 && response.Length >= length)
|
||||
{
|
||||
Array.Copy(response, 1, this.bytes, 0, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Status Status { get; set; }
|
||||
|
||||
public bool Succeeded => this.Status == Status.Completed;
|
||||
|
||||
public static implicit operator byte[](FeatureResponse response)
|
||||
=> response?.bytes ?? new byte[0];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,324 @@
|
||||
namespace OmniPlus.Features
|
||||
{
|
||||
using static System.Collections.Specialized.BitVector32;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Policy;
|
||||
using System;
|
||||
|
||||
internal class Features
|
||||
{
|
||||
/// <summary>
|
||||
/// The View Factory ID command shall cause the slave device to transmit the factory ID.
|
||||
/// The CDU shall consist of the command byte.
|
||||
/// The slave device shall respond by sending a message containing a status byte followed by the ASCII ID string
|
||||
/// terminated by a NULL character.
|
||||
/// <para>Note: This value is reported in the ;K field of UI-1203’s AMR string.</para>
|
||||
/// </summary>
|
||||
public const byte VIEW_FACTORY_ID = 0x01;
|
||||
/// <summary>
|
||||
/// The Set Factory ID command shall set the device ID.
|
||||
/// This command shall only be executed at the factory.
|
||||
/// The CDU shall consist of the one-byte command field followed by one to twelve ASCII alphanumeric characters.
|
||||
/// The ID shall be NULL terminated.
|
||||
/// The length of the ID string is device dependent.
|
||||
/// <para>It is recommended that all devices implement this command, though it may be protected by a factory seal.</para>
|
||||
/// </summary>
|
||||
public const byte SET_FACTORY_ID = 0x02;
|
||||
|
||||
/// <summary>
|
||||
/// The View Programmable ID command shall cause the slave device to return the customer ID.
|
||||
/// The CDU shall consist of the command byte.
|
||||
/// The slave device shall transmit a message block containing a NULL terminated ASCII string
|
||||
/// representing the customer ID.
|
||||
/// If the ID is NULL, a single NULL character (hex 00) shall be returned in the response string.
|
||||
/// <para>Note: This is the ID that is always reported in the UI-1203 AMR string.</para>
|
||||
/// </summary>
|
||||
public const byte VIEW_PROGRAMMABLE_ID = 0x03;
|
||||
/// <summary>
|
||||
/// The Set Programmable ID command shall set the customer ID in the slave device.
|
||||
/// The CDU shall consist of the one byte command code followed by a NULL terminated ASCII string
|
||||
/// of 1-20 alphanumeric characters.
|
||||
/// The ID shall be cleared and set to NULL if this command is sent with a string consisting
|
||||
/// of a single NULL (hex 00) character.
|
||||
/// <para>
|
||||
/// Note: This sets the ID that is required to be reported in the UI-1203 AMR string.
|
||||
/// In Fixed Length reading mode, the length is restricted to 8 characters.
|
||||
/// In Variable Length reading mode, the ;I field is limited to 12 characters.
|
||||
/// Some devices my reject long IDs based on the UI-1203 restrictions.
|
||||
/// </para>
|
||||
/// <para>It is recommended that all devices implement this command.</para>
|
||||
/// </summary>
|
||||
public const byte SET_PROGRAMMABLE_ID = 0x04;
|
||||
|
||||
/// <summary>
|
||||
/// The View Version and Type command is used to determine the type of device being programmed.
|
||||
/// The CDU shall consist of the command byte.
|
||||
/// The slave device shall respond by sending data containing the protocol version, device type and software version.
|
||||
/// The format of the data is three comma-delimited strings with a single NULL terminator.
|
||||
/// [Protocol version],[Device type],[Software version]
|
||||
/// Each string consists of a maximum of eight characters.
|
||||
/// </summary>
|
||||
public const byte VIEW_VERSION_AND_TYPE = 0x05;
|
||||
|
||||
/// <summary>
|
||||
/// The View Customer Text command shall cause the slave device to return a string containing the customer text field.
|
||||
/// The CDU shall consist of the command byte.
|
||||
/// The slave device shall transmit a message block containing a NULL terminated ASCII string
|
||||
/// representing the customer text field.
|
||||
/// If the ID is NULL, a single NULL character (hex 00) shall be returned in the response string.
|
||||
/// <para>Note: This is the value reported in the ;J field of the UI-1203 AMR string.</para>
|
||||
/// </summary>
|
||||
public const byte VIEW_CUSTOMER_TEXT = 0x07;
|
||||
/// <summary>
|
||||
/// The Set Customer Text command shall set the Customer Text field in the slave device.
|
||||
/// The CDU shall consist of the one byte command code followed by a NULL terminated ASCII string
|
||||
/// of 1-20 alphanumeric characters.
|
||||
/// The Customer Text field shall be cleared and set to NULL if this command is sent with a string consisting
|
||||
/// of a single NULL (hex 00) character.
|
||||
/// <para>Note: This is the value reported in the ;J field of the UI-1203 AMR string.</para>
|
||||
/// </summary>
|
||||
public const byte SET_CUSTOMER_TEXT = 0x08;
|
||||
|
||||
/// <summary>
|
||||
/// The View Reading Digits command shall cause the slave device to return the number of reading digits.
|
||||
/// The slave device shall transmit a message block containing two 8-bit binary numbers:
|
||||
/// <para>
|
||||
/// 0: The first number represents the number of reading digits to report in the AMR string, from 4 to 9.
|
||||
/// If 9, then the; NB field will be enabled in the AMR string.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// 1: The second number represents the AMR Reading Offset,
|
||||
/// which indicates the offset into the totalizer value where the AMR reading begins,
|
||||
/// counting from the right.The value is either 0 or negative.A value of 0 means to discard no digits.
|
||||
/// A value of -1 means to discard the least significant digit, etc.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// For example, if the totalizer on the meter shows 876543210,
|
||||
/// and the number of reading digits is 5 with an AMR Reading Offset of -2 (0xFE),
|
||||
/// the AMR reading reported will be 65432.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Note that previously, the AMR Reading Offset was defined to be the number of digits to the right of the decimal point
|
||||
/// being reported, but no known devices implemented this command that way,
|
||||
/// since the position of the decimal point was unknown (electronically) on mechanical registers.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public const byte VIEW_READING_DIGITS = 0x09;
|
||||
/// <summary>
|
||||
/// The Set Number of Reading Digits command shall set the number of reading digits reported in the AMR string.
|
||||
/// The CDU shall consist of the one-byte command code followed by two 8-bit binary numbers.
|
||||
/// The first octet represents the total number of digits in the reading string.
|
||||
/// The second octet represents the AMR Reading Offset (see cmd 0x09).
|
||||
/// </summary>
|
||||
public const byte SET_READING_DIGITS = 0x0A;
|
||||
|
||||
/// <summary>
|
||||
/// The View Reading Units command shall cause the slave device to return the reading units.
|
||||
/// The CDU shall consist of the command byte.
|
||||
/// The slave device shall transmit an eight bit binary number representing the reading units.
|
||||
/// The table listing reading units and the associated number is contained in UI-1203.
|
||||
/// On devices that do not know their reading units, the value 255 (0xFF) will be returned.
|
||||
/// </summary>
|
||||
public const byte VIEW_READING_UNITS = 0x0B;
|
||||
/// <summary>
|
||||
/// The Set Reading Units command shall set the reading units for the slave device.
|
||||
/// The CDU shall consist of the one-byte command code followed by an eight bit binary number representing the reading units.
|
||||
/// The table listing the reading units and the associated number is contained in UI-1203.
|
||||
/// On some devices, sending the value 255 (0xFF) will disable the units subfield of the UI-1203 ; R field,
|
||||
/// on others the resolution and units subfields will be disabled.On some devices,
|
||||
/// sending the value 255 (0xFF) will result in an Unable to Execute response.
|
||||
/// On many devices, there is no error checking on the value, and it is possible to set the value to something invalid.
|
||||
/// </summary>
|
||||
public const byte SET_READING_UNITS = 0x0C;
|
||||
|
||||
/// <summary>
|
||||
/// The View Rate Units command shall cause the slave device to return the rate units.
|
||||
/// The CDU shall consist of the command byte.
|
||||
/// The slave device shall transmit two bytes representing the rate units.
|
||||
/// The first byte represents the totalization units and the second byte represents the time units.
|
||||
/// The table listing the rate units and the associated numbers is contained in UI-1203.
|
||||
/// </summary>
|
||||
public const byte VIEW_RATE_UNITS = 0x0D;
|
||||
/// <summary>
|
||||
/// The Set Rate Units command shall set the rate units for the slave device.
|
||||
/// The CDU shall consist of the one byte command code followed by two eight bit binary numbers
|
||||
/// representing the rate units.
|
||||
/// The first byte represents the volume units and the second byte represents the time units.
|
||||
/// The table listing the rate units and the associated numbers is contained in UI-1203.
|
||||
/// </summary>
|
||||
public const byte SET_RATE_UNITS = 0x0E;
|
||||
|
||||
/// <summary>
|
||||
/// The View Reading Resolution command shall cause the slave device to return the resolution setting for the reading.
|
||||
/// The CDU shall consist of the command byte.
|
||||
/// The slave device shall respond by sending a signed eight bit binary number representing
|
||||
/// the reading resolution in powers of ten.
|
||||
/// The value returned is the resolution reported in the UI-1203 read response’s ;R field,
|
||||
/// if the resolution subfield is enabled.
|
||||
/// On some devices, if the resolution subfield is disabled in the UI-1203 read response,
|
||||
/// this command will return -128 (0x80).
|
||||
/// On other devices, the resolution of the currently selected AMR digits is returned.
|
||||
/// </summary>
|
||||
public const byte VIEW_READING_RESOLUTION = 0x0F;
|
||||
/// <summary>
|
||||
/// The Set Reading Resolution command shall set the slave device resolution for the reading.
|
||||
/// The CDU consists of the command byte followed by a signed eight bit binary number representing
|
||||
/// the reading resolution in powers of ten.
|
||||
/// The maximum range of reading resolution is specified in UI-1203.
|
||||
/// Use this command to enable the resolution subfield of the UI-1203 ; R field.
|
||||
/// The value -128 (0x80) disables that subfield on most devices.On many devices,
|
||||
/// there is no error checking on this value, and what is set is what will be reported in the ; R field,
|
||||
/// even if it is incorrect.
|
||||
/// If your goal is to change the resolution of the reported reading,
|
||||
/// cmd 0x0A – Set Number of Reading Digits is most likely the command you need.
|
||||
/// </summary>
|
||||
public const byte SET_READING_RESOLUTION = 0x10;
|
||||
|
||||
/// <summary>
|
||||
/// The View Rate Resolution command shall cause the slave device to return the resolution setting for the rate.
|
||||
/// The CDU shall consist of the command byte.
|
||||
/// The slave device will send a response message with a signed eight bit binary number representing
|
||||
/// the rate resolution in powers of ten.
|
||||
/// </summary>
|
||||
public const byte VIEW_RATE_RESOLUTION = 0x11;
|
||||
/// <summary>
|
||||
/// The Set Rate Resolution command shall set the slave device resolution for rate.
|
||||
/// The CDU consists of the command code followed by a signed eight bit binary number representing
|
||||
/// the rate resolution in powers of ten.
|
||||
/// The maximum range of rate resolution is specified in UI-1203.
|
||||
/// </summary>
|
||||
public const byte SET_RATE_RESOLUTION = 0x12;
|
||||
|
||||
/// <summary>
|
||||
/// As of B1.23, there are two variants of this command:
|
||||
/// If the CDU consists of only the command byte.
|
||||
/// The slave device shall return a message block containing a NULL terminated ASCII string of one to eight characters.
|
||||
/// This version of the command existed in B1.22.
|
||||
/// In B1.23, the command was updated to allow for more than 8 digits and to support multiple sensors.
|
||||
/// The format of the B1.23 version of the command is shown in the table below.The response will be a
|
||||
/// null-terminated ASCII string.
|
||||
/// </summary>
|
||||
public const byte VIEW_READINGS = 0x13;
|
||||
|
||||
/// <summary>
|
||||
/// As of B1.23, there are two variants of this command:
|
||||
/// If the CDU consists of only the command byte.
|
||||
/// The slave device shall return a message block containing a NULL terminated ASCII string of one to eight characters.
|
||||
/// This version of the command existed in B1.22.
|
||||
/// In B1.23, the command was updated to allow for more than 8 digits and to support multiple sensors.
|
||||
/// The format of the B1.23 version of the command is shown in the table below.The response will be a
|
||||
/// null-terminated ASCII string.
|
||||
/// </summary>
|
||||
public const byte SET_READING_PRESET = 0x14;
|
||||
|
||||
/// <summary>
|
||||
/// The View Reading Mode command shall cause the slave device to return the current value of the reading mode.
|
||||
/// The CDU shall consist of the command byte. The slave device shall return a message block
|
||||
/// containing one byte binary number representing the reading mode.
|
||||
/// If the reading mode is not one of the formats specified in the description of cmd 0x16,
|
||||
/// the response for this command will be 0xFF to indicate Undefined.
|
||||
/// Command 0x27 which was introduced with B1.23 is preferable to this command as it is more flexible.
|
||||
/// </summary>
|
||||
public const byte VIEW_READING_MODE = 0x15;
|
||||
/// <summary>
|
||||
/// The Set Reading Mode command shall set the unidirectional reading mode in a register.
|
||||
/// The CDU shall consist of the command byte followed by a one byte binary number.
|
||||
/// The number represents the unidirectional reading mode setting as shown below in table three.
|
||||
/// Unidirectional reading modes and their respective fields are described
|
||||
/// in the communication protocol document(s) for each device.
|
||||
/// Some devices use different definitions than those found in the table above.
|
||||
/// Most device report the; R field before the; I field.
|
||||
/// Command 0x28 which was introduced with B1.23 is preferable to this command as it is more flexible.
|
||||
/// </summary>
|
||||
public const byte SET_READING_MODE = 0x16;
|
||||
|
||||
/// <summary>
|
||||
/// The View Build Information command shall cause the slave device to return a string containing the build information.
|
||||
/// The CDU shall consist of the command byte.
|
||||
/// The slave device shall transmit a message block containing a NULL terminated ASCII string
|
||||
/// representing the build information.
|
||||
/// If the ID is NULL, a single NULL character (hex 00) shall be returned in the response string.
|
||||
/// </summary>
|
||||
public const byte VIEW_BUILD_INFORMATION = 0x17;
|
||||
/// <summary>
|
||||
/// The Set Build Information command shall set the build Information field in the slave device.
|
||||
/// The CDU shall consist of the one byte command code followed by a NULL terminated ASCII string
|
||||
/// of 1-14 alphanumeric characters.
|
||||
/// The Build Info field shall be cleared and set to NULL if this command is sent with a string consisting of
|
||||
/// a single NULL (hex 00) character.
|
||||
/// Build information may vary from product to product.
|
||||
/// Build information may include, but not be limited to, date, time, manufacturing facility code,
|
||||
/// production line code, etc.
|
||||
/// Each project should detail build information data in its respective communication protocol specification document.
|
||||
/// </summary>
|
||||
public const byte SET_BUILD_INFORMATION = 0x18;
|
||||
|
||||
public const byte DEVICE_SPECIFIC = 0xFD;
|
||||
|
||||
/// <summary>
|
||||
/// Command is provided to allow the Factory to view multiple System Configurations with one
|
||||
/// UI1236 command to save time during Factory Configuration.
|
||||
/// </summary>
|
||||
public const byte VIEW_SYSTEM_PARAMETERS = 0xC6;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Command is provided to allow the Factory to view multiple Configurations with
|
||||
/// one UI1236 command to save time during Factory Configuration.
|
||||
/// Command is configured to take 0 to 1 parameters.
|
||||
/// To keep the command backward compatible with previous versions of the command,
|
||||
/// if no parameters are provided, the command will generate a response
|
||||
/// with all of the response elements up to View Device ID String but no including the View ID String.
|
||||
/// </summary>
|
||||
public const byte VIEW_CONFIGURATION_PARAMETERS = 0xC7;
|
||||
|
||||
/// <summary>
|
||||
/// View volume per pulse.
|
||||
/// Volume per pulse is the amount of volume (in units of milliliters) that is added to the volume counter
|
||||
/// when one pulse of a meter shaft magnet is detected.
|
||||
/// (Commonly meter shaft magnets have two or four magnetic poles and produce four or eight pulses per full rotation.)
|
||||
/// </summary>
|
||||
public const byte VIEW_INPUT_VOLUME = 0x02;
|
||||
/// <summary>
|
||||
/// Set volume per pulse.
|
||||
/// </summary>
|
||||
public const byte SET_INPUT_VOLUME = 0x03;
|
||||
|
||||
/// <summary>
|
||||
/// Command is used to test the accuracy of meter and to calibrate the Meter Register assembly.
|
||||
/// This is a factory command and is only available when the Register is not Factory Sealed.
|
||||
/// The time to execute the command is determined by the present flow on the meter when
|
||||
/// the command was sent and how many rotations are requested to be collected.
|
||||
/// Since the time is not known by the Register, it is up to the user to provide a maximum time the command will execute.
|
||||
/// If the test does not complete by the maximum time, a failure response will be generated when the Test Results are requested.
|
||||
/// Once the command is sent to execute the calibration procedure,
|
||||
/// the user will receive a response indicating that the test has started and then the Register will perform the test.
|
||||
/// While the test is being performed, the Register will be unresponsive until the test has completed.
|
||||
/// The user must wait the appropriate time to before requesting the results.
|
||||
/// Based on the flow rate and the number of samples it takes to complete the test,
|
||||
/// the user should be able to calculate how long to wait before requesting results.
|
||||
/// </summary>
|
||||
public const byte CHAMBER_CALIBRATION = 0x67;
|
||||
|
||||
/// <summary>
|
||||
/// System time (a.k.a. epoch time) is the number of seconds from midnight January 1, 2000 UTC.
|
||||
/// </summary>
|
||||
public const byte VIEW_SYSTEM_TIME = 0x10;
|
||||
/// <summary>
|
||||
/// After powering up system time starts at midnight January 2, 2000 UTC/GMT; i.e. 86400 seconds from January 1, 2000 epoch.
|
||||
/// System time can be advanced forward, but cannot be changed into the past.
|
||||
/// Setting system time to 0 while the register is not sealed causes the following to happen:
|
||||
/// <para>• Volume data log is cleared</para>
|
||||
/// <para>• Epoch time and volume reading are cleared</para>
|
||||
/// <para>• Periodic alarm log is cleared</para>
|
||||
/// <para>• Alarm counters are cleared</para>
|
||||
/// <para>• Processor is reset(command times out).</para>
|
||||
/// <para>• Upon reset system time is 86400 seconds(24 hours, January 2, 2000)</para>
|
||||
/// When system time is successfully changed by this command (except when it is set to 0),
|
||||
/// a new boot recovery record is written into flash.This way time,
|
||||
/// current volume reading and alarm counts can be recovered if Set System Time is followed by a reboot.
|
||||
/// </summary>
|
||||
public const byte SET_SYSTEM_TIME = 0x11;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
|
||||
namespace OmniPlus.Features
|
||||
{
|
||||
public class InputVolume : FeatureRequest
|
||||
{
|
||||
const int LENGTH = 6;
|
||||
|
||||
public InputVolume() : base(LENGTH)
|
||||
{
|
||||
}
|
||||
|
||||
public InputVolume(byte[] response) : base(response, LENGTH)
|
||||
{
|
||||
}
|
||||
|
||||
public ushort IntPart
|
||||
{
|
||||
get => BitConverter.ToUInt16(this.bytes, 0);
|
||||
set => BitConverter.GetBytes(value).CopyTo(this.bytes, 0);
|
||||
}
|
||||
|
||||
public ushort Fraction
|
||||
{
|
||||
get => BitConverter.ToUInt16(this.bytes, 2);
|
||||
set => BitConverter.GetBytes(value).CopyTo(this.bytes, 2);
|
||||
}
|
||||
|
||||
public byte Size
|
||||
{
|
||||
get => this.bytes[4];
|
||||
set => this.bytes[4] = value;
|
||||
}
|
||||
|
||||
public byte Range
|
||||
{
|
||||
get => this.bytes[5];
|
||||
set => this.bytes[5] = value;
|
||||
}
|
||||
|
||||
public int Factor { get; set; } = 100;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace OmniPlus.Features
|
||||
{
|
||||
public class ManifacturingTime : Epoch20000101
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace OmniPlus.Features
|
||||
{
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// TODO: Need more investigation of the purpose
|
||||
/// </summary>
|
||||
public class OptionalUniDirFields : FeatureRequest
|
||||
{
|
||||
const int LENGTH = 4;
|
||||
|
||||
public OptionalUniDirFields() : base(LENGTH)
|
||||
{
|
||||
}
|
||||
|
||||
public OptionalUniDirFields(byte[] response) : base(response, LENGTH)
|
||||
{
|
||||
}
|
||||
|
||||
public void Past(byte[] bytes, int start)
|
||||
{
|
||||
this.bytes.Clear();
|
||||
|
||||
Array.Copy(bytes, start, this.bytes, 0, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace OmniPlus.Features
|
||||
{
|
||||
using System.Text;
|
||||
|
||||
public class ProgrammableID : ASCIIString
|
||||
{
|
||||
const int LENGTH = 20;
|
||||
|
||||
public ProgrammableID() : base(LENGTH)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public ProgrammableID(byte[] response) : base(response, LENGTH)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
|
||||
namespace OmniPlus.Features
|
||||
{
|
||||
/// <summary>
|
||||
/// Rate resolution depends on the reading units and the size of the meter.
|
||||
/// The customer is allowed to change the units of measure but they are not allowed to change Meter Range
|
||||
/// which indicates the actual size of the meter.
|
||||
/// </summary>
|
||||
public class RateResolution : FeatureRequest
|
||||
{
|
||||
const int LENGTH = 2;
|
||||
|
||||
public RateResolution() : base(LENGTH)
|
||||
{
|
||||
}
|
||||
|
||||
public RateResolution(byte[] response) : base(response, LENGTH)
|
||||
{
|
||||
}
|
||||
|
||||
public sbyte Value
|
||||
{
|
||||
get => (sbyte)BitConverter.ToUInt16(this.bytes, 0);
|
||||
set => BitConverter
|
||||
.GetBytes(value)
|
||||
.CopyTo(this.bytes, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* TODO: Implement the rate resolution table below:
|
||||
* ------------------------------------------------------------------
|
||||
* Meter Range Cubic Meters Cubic Feet Gallons Acre Feet
|
||||
* ------------------------------------------------------------------
|
||||
* m³/h cf/min gal/min
|
||||
* igal/min
|
||||
* ------------------------------------------------------------------
|
||||
* 1 ½” or 2” -3 -3 -3 Not Supported
|
||||
* 3” or 4” Extended -3 -3 -3 Not Supported
|
||||
* 3” or 4” Normal -3 -3 -3 Not Supported
|
||||
* 6” -3 -2 -2 Not Supported
|
||||
* 8” -2 -2 -2 Not Supported
|
||||
* 10” -2 -2 -2 Not Supported
|
||||
*/
|
||||
@@ -0,0 +1,52 @@
|
||||
namespace OmniPlus.Features
|
||||
{
|
||||
using OmniPlus.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// Omni+ flow rate units are independent of the reading units.
|
||||
/// <para>Note: Acre Feet per Hour is not supported.</para>
|
||||
/// </summary>
|
||||
public class RateUnits : FeatureRequest
|
||||
{
|
||||
const int LENGTH = 2;
|
||||
|
||||
public RateUnits() : base(LENGTH)
|
||||
{
|
||||
}
|
||||
|
||||
public RateUnits(byte[] response) : base(response, LENGTH)
|
||||
{
|
||||
}
|
||||
|
||||
public MessurementUnits Flow
|
||||
{
|
||||
get => (MessurementUnits)this.bytes[0];
|
||||
set
|
||||
{
|
||||
var flow = (byte)MessurementUnits.CubicMeters;
|
||||
var time = (byte)TimeUnits.Hours;
|
||||
|
||||
switch (value)
|
||||
{
|
||||
case MessurementUnits.CubicFeet:
|
||||
flow = (byte)value;
|
||||
time = (byte)TimeUnits.Minutes;
|
||||
break;
|
||||
case MessurementUnits.USGallons:
|
||||
flow = (byte)value;
|
||||
time = (byte)TimeUnits.Minutes;
|
||||
break;
|
||||
case MessurementUnits.ImperialGallons:
|
||||
flow = (byte)value;
|
||||
time = (byte)TimeUnits.Minutes;
|
||||
break;
|
||||
}
|
||||
|
||||
this.bytes[0] = flow;
|
||||
this.bytes[1] = time;
|
||||
}
|
||||
}
|
||||
|
||||
public TimeUnits Time => (TimeUnits)this.bytes[1];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace OmniPlus.Features
|
||||
{
|
||||
public class ReadingDigits : FeatureRequest
|
||||
{
|
||||
const int LENGTH = 2;
|
||||
|
||||
public ReadingDigits() : base(LENGTH)
|
||||
{
|
||||
}
|
||||
|
||||
public ReadingDigits(byte[] response) : base(response, LENGTH)
|
||||
{
|
||||
}
|
||||
|
||||
public byte Count
|
||||
{
|
||||
get => this.bytes[0];
|
||||
set => this.bytes[0] = value;
|
||||
}
|
||||
|
||||
public byte Offset
|
||||
{
|
||||
get => this.bytes[1];
|
||||
set => this.bytes[1] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace OmniPlus.Features
|
||||
{
|
||||
/// <summary>
|
||||
/// Response will either indicate Fixed or Undefined (like ally). Use cmd 0x27 instead.
|
||||
/// Fixed mode requires a Programmable ID of exactly 8 characters and exactly 4 AMR digits.
|
||||
/// If Programmable ID or number of AMR digits is invalid, command will return Unable to Execute.
|
||||
/// The following actions will cause the register to exit Fixed mode:
|
||||
/// Setting the reading string fields with cmd 0x28 will exit fixed mode.
|
||||
/// Changing the Programmable ID to a length other than exactly 8 characters will exit fixed mode.
|
||||
/// Changing the number AMR digits to a value other than 4 will exit fixed mode.
|
||||
/// If programmable ID or number of AMR digits is set to something invalid for fixed mode,
|
||||
/// then to re - enter fixed mode requires correcting the changed parameter and sending command 0x16 Set Reading Mode again.
|
||||
/// </summary>
|
||||
public class ReadingMode : FeatureRequest
|
||||
{
|
||||
const int LENGTH = 13;
|
||||
|
||||
public ReadingMode() : base(LENGTH)
|
||||
{
|
||||
}
|
||||
|
||||
public ReadingMode(byte[] response) : base(response, LENGTH)
|
||||
{
|
||||
}
|
||||
|
||||
// TODO: To be implemented.
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using OmniPlus.Enums;
|
||||
|
||||
namespace OmniPlus.Features
|
||||
{
|
||||
/// <summary>
|
||||
/// Versions B1.24 of this command are supported. Total Forward and Total Reverse are not supported.
|
||||
/// Absence of command payload(i.e.of the reading preset) clears the Totalizer reading unless
|
||||
/// Display View is set to Totalizer Test View which sets the Test Totalizer Counter to 0.
|
||||
/// Command will support setting Totalizer Value, Test Totalizer Value and the Lifetime Totalizer value
|
||||
/// while the device is not factory sealed.
|
||||
/// If the device is factory sealed, the Lifetime Totalizer is prevented from being changed
|
||||
/// and Test Totalizer is limited to setting it to 0.
|
||||
/// The Set Reading Preset command shall send the value to be used as the initial reading.
|
||||
/// The CDU consists of a one-byte command code followed by a NULL terminated ASCII string of one to eight characters
|
||||
/// representing the preset reading value.This value is assumed to be in the current units used for totalization.
|
||||
/// In protocol version B1.23, 9 digits are allowed on devices that support it.
|
||||
/// Some devices that implement B1.22 may also support a 9-digit null-terminated string.
|
||||
/// </summary>
|
||||
public class ReadingPreset : FeatureRequest
|
||||
{
|
||||
const int LENGTH = 2;
|
||||
|
||||
public ReadingPreset() : base(LENGTH)
|
||||
{
|
||||
}
|
||||
|
||||
public byte SensorID
|
||||
{
|
||||
get => this.bytes[0];
|
||||
set => this.bytes[0] = value;
|
||||
}
|
||||
|
||||
public ReadingSettings Setting
|
||||
{
|
||||
get => (ReadingSettings)this.bytes[1];
|
||||
set => this.bytes[1] = (byte)value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
|
||||
namespace OmniPlus.Features
|
||||
{
|
||||
/// <summary>
|
||||
/// Reading Resolution of the device depends on reading units and the size of the Meter.
|
||||
/// The customer is allowed to change the Units of measure but they are not able to change
|
||||
/// the Reading Resolution for the Meter Range.
|
||||
/// </summary>
|
||||
public class ReadingResolution : FeatureRequest
|
||||
{
|
||||
const int LENGTH = 2;
|
||||
|
||||
public ReadingResolution() : base(LENGTH)
|
||||
{
|
||||
}
|
||||
|
||||
public ReadingResolution(byte[] response) : base(response, LENGTH)
|
||||
{
|
||||
}
|
||||
|
||||
public sbyte Value
|
||||
{
|
||||
get => (sbyte)BitConverter.ToUInt16(this.bytes, 0);
|
||||
set => BitConverter
|
||||
.GetBytes(value)
|
||||
.CopyTo(this.bytes, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: Implement the reading resolution table below:
|
||||
* -----------------------------------------------------------------------------------------------
|
||||
* Meter Range Cubic Meters Cubic Feet Imperial Gallons Gallons Acre Feet
|
||||
* -----------------------------------------------------------------------------------------------
|
||||
* 1 ½” or 2” -3 -2 0 0 -5
|
||||
* 3” or 4” Extended -2 -2 0 0 -5
|
||||
* 3” or 4” Normal -2 0 0 0 -5
|
||||
* 6” -2 0 0 0 -5
|
||||
* 8” -2 0 0 0 -4
|
||||
*/
|
||||
@@ -0,0 +1,31 @@
|
||||
using OmniPlus.Enums;
|
||||
|
||||
namespace OmniPlus.Features
|
||||
{
|
||||
/// <summary>
|
||||
/// 0xFF in the reading units byte does not change the reading units.
|
||||
/// 0xFF disables reporting of multiplier and units in the reading and flowrate uni-directional fields.
|
||||
/// Setting reading units to one of the actual supported values,
|
||||
/// enables reporting of multiplier and units in the reading and flowrate uni-directional fields.
|
||||
/// Reading Resolution is based on Meter Range and Units of Measure.
|
||||
/// Omni+ does not support a separate command to set reading resolution.
|
||||
/// </summary>
|
||||
public class ReadingUnits : FeatureRequest
|
||||
{
|
||||
const int LENGTH = 1;
|
||||
|
||||
public ReadingUnits() : base(LENGTH)
|
||||
{
|
||||
}
|
||||
|
||||
public ReadingUnits(byte[] response) : base(response, LENGTH)
|
||||
{
|
||||
}
|
||||
|
||||
public MessurementUnits Value
|
||||
{
|
||||
get => (MessurementUnits)this.bytes[0];
|
||||
set => this.bytes[0] = (byte)value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using OmniPlus.Enums;
|
||||
|
||||
using System.Text;
|
||||
|
||||
namespace OmniPlus.Features
|
||||
{
|
||||
/// <summary>
|
||||
/// Versions B1.24 of this command are supported. Total Forward and Total Reverse are not supported.
|
||||
/// <para>
|
||||
/// If sensor filed is set to 0, command supports viewing Lifetime Totalizer Value(Setting of 3)
|
||||
/// and Test Totalizer Value(Setting of 4). If reading the Life Time totalizer value,
|
||||
/// the number of digits reported will be increased to 13 digits to support a larger Reading.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Requesting a reading from an invalid sensor ID or reading type will result in an “Unable to execute” response.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Omni supports the B1.24 version of the command which allows additional functionality
|
||||
/// if more data is supplied in the payload. If 2 or more bytes of the payload are provided,
|
||||
/// the first byte is used to indicate the sensor with 0 being a volume sensor.
|
||||
/// The second byte of the payload is which one of the specified type of sensor is being read.
|
||||
/// If a third byte of the payload is provided, the command will also provide the multiplier resolution of the sensor reading.
|
||||
/// Omni only supports the Volume Sensor.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Note: Because of the size of the Meter,
|
||||
/// the Life Time Totalizer may exceed the reportable range using the Resolution set for the Totalizer value.
|
||||
/// When the Life Time Totalizer value exceeds 12 reportable digits and the Resolution is not 0,
|
||||
/// the Resolution will be reduced until either the whole part of the Life Time Totalizer is reported
|
||||
/// or until there are no more Resolution steps to reduce the reading at which time the number
|
||||
/// will be truncated to 12 digits.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public class Readings : ASCIIString
|
||||
{
|
||||
const int LENGTH = 8;
|
||||
|
||||
public Readings() : base(LENGTH)
|
||||
{
|
||||
}
|
||||
|
||||
public Readings(byte[] response) : base(response, LENGTH)
|
||||
{
|
||||
}
|
||||
|
||||
public byte SensorID => this.bytes[0];
|
||||
|
||||
public ReadingSettings Setting => (ReadingSettings)this.bytes[1];
|
||||
|
||||
public bool Include => this.bytes[2] == 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
namespace OmniPlus.Features
|
||||
{
|
||||
using System;
|
||||
|
||||
public class RegisterSettings : FeatureRequest
|
||||
{
|
||||
const int LENGTH = 13;
|
||||
|
||||
public RegisterSettings() : base(LENGTH)
|
||||
{
|
||||
}
|
||||
|
||||
public RegisterSettings(byte[] response) : base(response, LENGTH)
|
||||
{
|
||||
}
|
||||
|
||||
public ushort IntPart
|
||||
{
|
||||
get => BitConverter.ToUInt16(this.bytes, 0);
|
||||
set => BitConverter.GetBytes(value).CopyTo(this.bytes, 0);
|
||||
}
|
||||
|
||||
public ushort Fraction
|
||||
{
|
||||
get => BitConverter.ToUInt16(this.bytes, 2);
|
||||
set => BitConverter.GetBytes(value).CopyTo(this.bytes, 2);
|
||||
}
|
||||
|
||||
public byte Size
|
||||
{
|
||||
get => this.bytes[4];
|
||||
set => this.bytes[4] = value;
|
||||
}
|
||||
|
||||
public byte Range
|
||||
{
|
||||
get => this.bytes[5];
|
||||
set => this.bytes[5] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
namespace OmniPlus.Features
|
||||
{
|
||||
using System;
|
||||
|
||||
public class SystemParameters : FeatureRequest
|
||||
{
|
||||
const int LENGTH = 59;
|
||||
|
||||
public SystemParameters() : base(LENGTH)
|
||||
{
|
||||
this.BuildInfo = new BuildInfo();
|
||||
this.ManifacturingTime = new ManifacturingTime();
|
||||
this.SystemTime = new SystemTime();
|
||||
this.VersionAndType = new VersionAndType();
|
||||
this.BatteryVoltage = new BatteryVoltage();
|
||||
}
|
||||
|
||||
public SystemParameters(byte[] response) : base(response, LENGTH)
|
||||
{
|
||||
this.BuildInfo = new BuildInfo();
|
||||
this.BuildInfo.Status = this.Status;
|
||||
this.BuildInfo.Value = this.bytes.ToASCIIString(0, 15);
|
||||
|
||||
this.ManifacturingTime = new ManifacturingTime();
|
||||
this.ManifacturingTime.Status = this.Status;
|
||||
this.ManifacturingTime.SecondsSince = BitConverter.ToUInt32(this.bytes, 16);
|
||||
|
||||
this.SystemTime = new SystemTime();
|
||||
this.SystemTime.Status = this.Status;
|
||||
this.SystemTime.SecondsSince = BitConverter.ToUInt32(this.bytes, 20);
|
||||
|
||||
this.RebootsCount = this.bytes[24];
|
||||
|
||||
this.VersionAndType = new VersionAndType();
|
||||
this.VersionAndType.Status = this.Status;
|
||||
this.VersionAndType.Value = this.bytes.ToASCIIString(25, 51);
|
||||
|
||||
this.BatteryVoltage = new BatteryVoltage();
|
||||
this.BatteryVoltage.Status = this.Status;
|
||||
this.BatteryVoltage.Volts = BitConverter.ToUInt16(this.bytes, 52);
|
||||
this.BatteryVoltage.ADCCount = BitConverter.ToUInt32(this.bytes, 54);
|
||||
|
||||
this.FactorySealed = this.bytes[58] == 1;
|
||||
}
|
||||
|
||||
public BuildInfo BuildInfo { get; set; }
|
||||
|
||||
public ManifacturingTime ManifacturingTime { get; set; }
|
||||
|
||||
public SystemTime SystemTime { get; set; }
|
||||
|
||||
public byte RebootsCount { get; set; }
|
||||
|
||||
public VersionAndType VersionAndType { get; set; }
|
||||
|
||||
public BatteryVoltage BatteryVoltage { get; set; }
|
||||
|
||||
public bool FactorySealed { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace OmniPlus.Features
|
||||
{
|
||||
public class SystemTime : Epoch20000101
|
||||
{
|
||||
public SystemTime()
|
||||
{
|
||||
}
|
||||
|
||||
public SystemTime(byte[] response) : base(response)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
namespace OmniPlus.Features
|
||||
{
|
||||
/// <summary>
|
||||
/// Protocol version is at least "R1.23".
|
||||
/// Device Type ID String is "OMNI-01".
|
||||
/// Software version follows the NA2W FlexNet format; e.g. "R1.0.0A".
|
||||
/// <para>
|
||||
/// Note: In R0.3.14 firmware the Device type string is no longer hardcoded to OMNI-01 in the firmware.
|
||||
/// Now the Factory can program a different string based on if it is a T2 or R2 variant.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public class VersionAndType : ASCIIString
|
||||
{
|
||||
const int LENGTH = 12;
|
||||
|
||||
public VersionAndType() : base(LENGTH)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public VersionAndType(byte[] response) : base(response, LENGTH)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
namespace OmniPlus.Hanlers
|
||||
{
|
||||
internal delegate void DataReceivedHandler(byte[] bytes);
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
namespace OmniPlus.Hanlers
|
||||
{
|
||||
internal delegate void LogMessageEventHandler(string message);
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
namespace OmniPlus.Hanlers
|
||||
{
|
||||
internal delegate byte[] MessageHandler(params byte[] bytes);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
using OmniPlus.Enums;
|
||||
|
||||
namespace OmniPlus.Hanlers
|
||||
{
|
||||
internal delegate void StateChangedEventHandler(SerialPortState state, string reason);
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{F8D84B9A-0FCF-4083-B067-8C39B7314152}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>OmniPlus</RootNamespace>
|
||||
<AssemblyName>OmniPlus</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.8.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<Deterministic>true</Deterministic>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Connections\IrdaConnection.cs" />
|
||||
<Compile Include="Enums\MessurementUnits.cs" />
|
||||
<Compile Include="Enums\ReadingSettings.cs" />
|
||||
<Compile Include="Enums\Status.cs" />
|
||||
<Compile Include="Enums\TimeUnits.cs" />
|
||||
<Compile Include="Features\ASCIIString.cs" />
|
||||
<Compile Include="Features\BatteryVoltage.cs" />
|
||||
<Compile Include="Features\BuildInfo.cs" />
|
||||
<Compile Include="Features\ChamberCalibration.cs" />
|
||||
<Compile Include="Features\ConfigurationParameters.cs" />
|
||||
<Compile Include="Features\CustomerText.cs" />
|
||||
<Compile Include="Features\DeviceID.cs" />
|
||||
<Compile Include="Features\Epoch20000101.cs" />
|
||||
<Compile Include="Features\FactoryID.cs" />
|
||||
<Compile Include="Features\Features.cs" />
|
||||
<Compile Include="Features\FeatureRequest.cs" />
|
||||
<Compile Include="Features\FeatureResponse.cs" />
|
||||
<Compile Include="Features\InputVolume.cs" />
|
||||
<Compile Include="Features\ManifacturingTime.cs" />
|
||||
<Compile Include="Features\OptionalUniDirFields.cs" />
|
||||
<Compile Include="Features\ProgrammableID.cs" />
|
||||
<Compile Include="Features\RateResolution.cs" />
|
||||
<Compile Include="Features\RateUnits.cs" />
|
||||
<Compile Include="Features\ReadingDigits.cs" />
|
||||
<Compile Include="Features\ReadingMode.cs" />
|
||||
<Compile Include="Features\ReadingPreset.cs" />
|
||||
<Compile Include="Features\Readings.cs" />
|
||||
<Compile Include="Features\ReadingResolution.cs" />
|
||||
<Compile Include="Features\ReadingUnits.cs" />
|
||||
<Compile Include="Features\RegisterSettings.cs" />
|
||||
<Compile Include="Features\SystemParameters.cs" />
|
||||
<Compile Include="Features\SystemTime.cs" />
|
||||
<Compile Include="Features\VersionAndType.cs" />
|
||||
<Compile Include="OmniPlusConnection.cs" />
|
||||
<Compile Include="Connections\SerialPortConnection.cs" />
|
||||
<Compile Include="Connections\UniProConnection.cs" />
|
||||
<Compile Include="Enums\BaudRate.cs" />
|
||||
<Compile Include="Enums\DataBits.cs" />
|
||||
<Compile Include="Enums\SerialPortState.cs" />
|
||||
<Compile Include="Hanlers\DataReceivedHandler.cs" />
|
||||
<Compile Include="Hanlers\LogMessageHandler.cs" />
|
||||
<Compile Include="Hanlers\MessageHandler.cs" />
|
||||
<Compile Include="Hanlers\StateChangedHandler.cs" />
|
||||
<Compile Include="OmniPlusConnectionsPool.cs" />
|
||||
<Compile Include="OmniPlusDatabase.cs" />
|
||||
<Compile Include="OmniPlusExtensions.cs" />
|
||||
<Compile Include="OmniPlusInspectionHeaders.cs" />
|
||||
<Compile Include="OmniPlusInspectionPoint.cs" />
|
||||
<Compile Include="OmniPlusMeter.cs" />
|
||||
<Compile Include="OmniPlusStateChangedEventHandler.cs" />
|
||||
<Compile Include="Options\OmniControlOptions.cs" />
|
||||
<Compile Include="Options\SerialPortOptions.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\NamedPipes\NamedPipes.csproj">
|
||||
<Project>{0CC5A97F-E1CD-412B-AFFE-B256FDB7DC19}</Project>
|
||||
<Name>NamedPipes</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
@@ -0,0 +1,276 @@
|
||||
using OmniPlus.Features;
|
||||
using OmniPlus.Hanlers;
|
||||
|
||||
using System;
|
||||
|
||||
using static OmniPlus.Features.Features;
|
||||
|
||||
namespace OmniPlus.Connections
|
||||
{
|
||||
public class OmniPlusConnection
|
||||
{
|
||||
private readonly IrdaConnection irdaConnection;
|
||||
private readonly UniProConnection uniProConnection;
|
||||
|
||||
public OmniPlusConnection(string portName)
|
||||
{
|
||||
this.irdaConnection = new IrdaConnection(portName);
|
||||
this.uniProConnection = new UniProConnection(portName);
|
||||
}
|
||||
|
||||
internal MessageHandler MessageHandler;
|
||||
|
||||
public bool ConnectIrda()
|
||||
{
|
||||
this.uniProConnection.Close();
|
||||
this.irdaConnection.Open();
|
||||
|
||||
this.MessageHandler = this.irdaConnection.SendMessage;
|
||||
|
||||
return this.irdaConnection.Connected;
|
||||
}
|
||||
|
||||
public bool ConnectUniPro()
|
||||
{
|
||||
this.irdaConnection.Close();
|
||||
this.uniProConnection.Open();
|
||||
|
||||
this.MessageHandler = this.uniProConnection.SendMessage;
|
||||
|
||||
return this.irdaConnection.Connected;
|
||||
}
|
||||
|
||||
public void Disconnect()
|
||||
{
|
||||
this.uniProConnection.Close();
|
||||
this.irdaConnection.Close();
|
||||
}
|
||||
|
||||
public FactoryID ViewFactoryID()
|
||||
{
|
||||
var response = this.MessageHandler.Invoke(VIEW_FACTORY_ID);
|
||||
|
||||
return new FactoryID(response);
|
||||
}
|
||||
|
||||
public FeatureResponse SetFactoryID(FactoryID factoryID)
|
||||
{
|
||||
var response = this.MessageHandler.Invoke(factoryID[SET_FACTORY_ID]);
|
||||
|
||||
return new FeatureResponse(response);
|
||||
}
|
||||
|
||||
public ProgrammableID ViewProgrammableID()
|
||||
{
|
||||
var response = this.MessageHandler.Invoke(VIEW_PROGRAMMABLE_ID);
|
||||
|
||||
return new ProgrammableID(response);
|
||||
}
|
||||
|
||||
public FeatureResponse SetProgrammableID(ProgrammableID programmableID)
|
||||
{
|
||||
var response = this.MessageHandler.Invoke(programmableID[SET_PROGRAMMABLE_ID]);
|
||||
|
||||
return new FeatureResponse(response);
|
||||
}
|
||||
|
||||
public VersionAndType ViewVersionAndType()
|
||||
{
|
||||
var response = this.MessageHandler.Invoke(VIEW_VERSION_AND_TYPE);
|
||||
|
||||
return new VersionAndType(response);
|
||||
}
|
||||
|
||||
public CustomerText ViewCustomerText()
|
||||
{
|
||||
var response = this.MessageHandler.Invoke(VIEW_CUSTOMER_TEXT);
|
||||
|
||||
return new CustomerText(response);
|
||||
}
|
||||
|
||||
public FeatureResponse SetCustomerText(CustomerText customerText)
|
||||
{
|
||||
var response = this.MessageHandler.Invoke(customerText[SET_CUSTOMER_TEXT]);
|
||||
|
||||
return new FeatureResponse(response);
|
||||
}
|
||||
|
||||
public ReadingDigits ViewReadingDigits()
|
||||
{
|
||||
var response = this.MessageHandler.Invoke(VIEW_READING_DIGITS);
|
||||
|
||||
return new ReadingDigits(response);
|
||||
}
|
||||
|
||||
public FeatureResponse SetReadingDigits(ReadingDigits readingDigits)
|
||||
{
|
||||
var response = this.MessageHandler.Invoke(readingDigits[SET_READING_DIGITS]);
|
||||
|
||||
return new FeatureResponse(response);
|
||||
}
|
||||
|
||||
public ReadingUnits ViewReadingUnits()
|
||||
{
|
||||
var response = this.MessageHandler.Invoke(VIEW_READING_UNITS);
|
||||
|
||||
return new ReadingUnits(response);
|
||||
}
|
||||
|
||||
public FeatureResponse SetReadingUnits(ReadingUnits readingUnits)
|
||||
{
|
||||
var response = this.MessageHandler.Invoke(readingUnits[SET_READING_UNITS]);
|
||||
|
||||
return new FeatureResponse(response);
|
||||
}
|
||||
|
||||
public RateUnits ViewRateUnits()
|
||||
{
|
||||
var response = this.MessageHandler.Invoke(VIEW_RATE_UNITS);
|
||||
|
||||
return new RateUnits(response);
|
||||
}
|
||||
|
||||
public FeatureResponse SetRateUnits(RateUnits rateUnits)
|
||||
{
|
||||
var response = this.MessageHandler.Invoke(rateUnits[SET_RATE_UNITS]);
|
||||
|
||||
return new FeatureResponse(response);
|
||||
}
|
||||
|
||||
public ReadingResolution ViewReadingResolution()
|
||||
{
|
||||
var response = this.MessageHandler.Invoke(VIEW_READING_RESOLUTION);
|
||||
|
||||
return new ReadingResolution(response);
|
||||
}
|
||||
|
||||
public FeatureResponse SetReadingResolution(ReadingResolution readingResolution)
|
||||
{
|
||||
var response = this.MessageHandler.Invoke(readingResolution[SET_READING_RESOLUTION]);
|
||||
|
||||
return new FeatureResponse(response);
|
||||
}
|
||||
|
||||
public RateResolution ViewRateResolution()
|
||||
{
|
||||
var response = this.MessageHandler.Invoke(VIEW_RATE_RESOLUTION);
|
||||
|
||||
return new RateResolution(response);
|
||||
}
|
||||
|
||||
public FeatureResponse SetRateResolution(RateResolution rateResolution)
|
||||
{
|
||||
var response = this.MessageHandler.Invoke(rateResolution[SET_READING_RESOLUTION]);
|
||||
|
||||
return new FeatureResponse(response);
|
||||
}
|
||||
|
||||
public Readings ViewReadings(Readings reading)
|
||||
{
|
||||
var response = this.MessageHandler.Invoke(VIEW_READINGS);
|
||||
|
||||
return new Readings(response);
|
||||
}
|
||||
|
||||
public FeatureResponse SetReadingPreset(ReadingPreset readingPreset)
|
||||
{
|
||||
var response = this.MessageHandler.Invoke(readingPreset[SET_READING_PRESET]);
|
||||
|
||||
return new FeatureResponse(response);
|
||||
}
|
||||
|
||||
public ReadingMode ViewReadingMode()
|
||||
{
|
||||
var response = this.MessageHandler.Invoke(VIEW_READING_MODE);
|
||||
|
||||
return new ReadingMode(response);
|
||||
}
|
||||
|
||||
public FeatureResponse SetReadingMode(ReadingMode readingMode)
|
||||
{
|
||||
var response = this.MessageHandler.Invoke(readingMode[SET_READING_MODE]);
|
||||
|
||||
return new FeatureResponse(response);
|
||||
}
|
||||
|
||||
public BuildInfo ViewBuildInformation()
|
||||
{
|
||||
var response = this.MessageHandler.Invoke(VIEW_BUILD_INFORMATION);
|
||||
|
||||
return new BuildInfo(response);
|
||||
}
|
||||
|
||||
public FeatureResponse SetBuildInformation(BuildInfo buildInformation)
|
||||
{
|
||||
var response = this.MessageHandler.Invoke(buildInformation[SET_BUILD_INFORMATION]);
|
||||
|
||||
return new FeatureResponse(response);
|
||||
}
|
||||
|
||||
public SystemParameters ViewSystemParameters()
|
||||
{
|
||||
var response = this.MessageHandler.Invoke(DEVICE_SPECIFIC, VIEW_SYSTEM_PARAMETERS);
|
||||
|
||||
return new SystemParameters(response);
|
||||
}
|
||||
|
||||
public ConfigurationParameters ViewConfigurationParameters()
|
||||
{
|
||||
var response = this.MessageHandler.Invoke(DEVICE_SPECIFIC, VIEW_CONFIGURATION_PARAMETERS, 0x01);
|
||||
|
||||
return new ConfigurationParameters(response);
|
||||
}
|
||||
|
||||
public InputVolume SetInputVolume()
|
||||
{
|
||||
var response = this.MessageHandler.Invoke(DEVICE_SPECIFIC, VIEW_INPUT_VOLUME);
|
||||
|
||||
return new InputVolume(response);
|
||||
}
|
||||
|
||||
public FeatureResponse SetInputVolume(InputVolume inputVolume)
|
||||
{
|
||||
var response = this.MessageHandler.Invoke(inputVolume[DEVICE_SPECIFIC, SET_INPUT_VOLUME]);
|
||||
|
||||
return new FeatureResponse(response);
|
||||
}
|
||||
|
||||
public ChamberCalibration StartChamberCalibration(ushort rotations, ushort duration)
|
||||
{
|
||||
var response = this.MessageHandler.Invoke(new ChamberCalibration
|
||||
{
|
||||
Rotations = rotations,
|
||||
Duration = duration,
|
||||
Control = ChamberCalibration.ACTIVATE_TEST
|
||||
});
|
||||
|
||||
return new ChamberCalibration(response);
|
||||
}
|
||||
|
||||
public ChamberCalibration ViewChamberCalibration()
|
||||
{
|
||||
var response = this.MessageHandler.Invoke(DEVICE_SPECIFIC, CHAMBER_CALIBRATION, ChamberCalibration.REQUEST_RESULTS);
|
||||
|
||||
return new ChamberCalibration(response);
|
||||
}
|
||||
|
||||
internal SystemTime ViewSystemTime()
|
||||
{
|
||||
var response = this.MessageHandler.Invoke(DEVICE_SPECIFIC, VIEW_SYSTEM_TIME);
|
||||
|
||||
return new SystemTime(response);
|
||||
}
|
||||
|
||||
internal FeatureResponse SetSystemTime(SystemTime systemTime = null)
|
||||
{
|
||||
if (systemTime is null)
|
||||
{
|
||||
systemTime = new SystemTime();
|
||||
}
|
||||
|
||||
var response = this.MessageHandler.Invoke(systemTime[DEVICE_SPECIFIC, SET_SYSTEM_TIME]);
|
||||
|
||||
return new FeatureResponse(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,351 @@
|
||||
using NamedPipes;
|
||||
|
||||
using OmniPlus.Connections;
|
||||
using OmniPlus.Features;
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Runtime.Remoting.Messaging;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using static OmniPlus.UI.Inspection.OmniPlusInspectionHeaders;
|
||||
|
||||
namespace OmniPlus.UI.Inspection
|
||||
{
|
||||
public class OmniPlusConnectionsPool : IDisposable
|
||||
{
|
||||
public event OmniPlusStateChangedEventHandler StateChanged;
|
||||
|
||||
private readonly ConcurrentDictionary<int, OmniPlusConnection> connections;
|
||||
private readonly ConcurrentDictionary<int, OmniPlusMeter> meters;
|
||||
private readonly ConcurrentDictionary<int, Task> tasks;
|
||||
private readonly NamedPipeClient namedPipeClient;
|
||||
private readonly OmniPlusDatabase database;
|
||||
|
||||
private bool available;
|
||||
private int size;
|
||||
|
||||
public OmniPlusConnectionsPool(string dbConnectionString)
|
||||
{
|
||||
this.connections = new ConcurrentDictionary<int, OmniPlusConnection>();
|
||||
this.meters = new ConcurrentDictionary<int, OmniPlusMeter>();
|
||||
this.namedPipeClient = new NamedPipeClient();
|
||||
this.database = new OmniPlusDatabase(dbConnectionString);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (this.StateChanged != null)
|
||||
{
|
||||
foreach (var callback in this.StateChanged.GetInvocationList())
|
||||
{
|
||||
if (callback is OmniPlusStateChangedEventHandler stateChangedCallback)
|
||||
{
|
||||
this.StateChanged -= stateChangedCallback;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var connection in this.connections.Values)
|
||||
{
|
||||
connection?.Disconnect();
|
||||
}
|
||||
|
||||
this.namedPipeClient.Connected -= this.NamedPipeClient_Connected;
|
||||
this.namedPipeClient.Disconnected -= this.NamedPipeClient_Disconnected;
|
||||
|
||||
this.namedPipeClient.Disconnect();
|
||||
this.connections.Clear();
|
||||
this.meters.Clear();
|
||||
}
|
||||
|
||||
public void InitializeInspection(params string[] portNames)
|
||||
{
|
||||
this.namedPipeClient.Connected += this.NamedPipeClient_Connected;
|
||||
this.namedPipeClient.Disconnected += this.NamedPipeClient_Disconnected;
|
||||
this.namedPipeClient.Connect();
|
||||
|
||||
OmniPlusConnection connection;
|
||||
OmniPlusMeter meter;
|
||||
var connections = 0;
|
||||
var position = 1;
|
||||
|
||||
foreach (var portName in portNames)
|
||||
{
|
||||
connection = new OmniPlusConnection(portName);
|
||||
meter = new OmniPlusMeter();
|
||||
|
||||
if (connection.ConnectIrda())
|
||||
{
|
||||
meter.Connected = true;
|
||||
connections++;
|
||||
}
|
||||
else
|
||||
{
|
||||
connection.Disconnect();
|
||||
connection = default(OmniPlusConnection);
|
||||
meter = default(OmniPlusMeter);
|
||||
}
|
||||
|
||||
this.meters.TryAdd(position, meter);
|
||||
this.connections.TryAdd(position, connection);
|
||||
this.StateChanged?.Invoke(position, true, SLOT_NR, position.ToString());
|
||||
|
||||
position++;
|
||||
}
|
||||
|
||||
if (this.available)
|
||||
{
|
||||
this.available = connections > 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void StartInspection()
|
||||
{
|
||||
this.StartOmniInitialization();
|
||||
this.StartOmniCalibration();
|
||||
this.FinishOmniInspection();
|
||||
}
|
||||
|
||||
private void StartOmniInitialization()
|
||||
{
|
||||
var factory = Task.Factory;
|
||||
|
||||
foreach (var position in this.connections.Keys)
|
||||
{
|
||||
this.tasks[position] = factory.StartNew(() => this.StartOmniInitialization(position));
|
||||
}
|
||||
|
||||
factory.ContinueWhenAll(this.tasks.Values.ToArray(), _ => { });
|
||||
}
|
||||
|
||||
private void StartOmniInitialization(int position)
|
||||
{
|
||||
if (this.TryGetOmni(position, out var connection, out var meter))
|
||||
{
|
||||
this.StateChanged?.Invoke(position, meter.Connected, CONNECTED, meter.Connected ? OK : X);
|
||||
|
||||
var systemParameters = connection.ViewSystemParameters();
|
||||
meter.Initialized = systemParameters.Succeeded;
|
||||
|
||||
meter.AddSystemParameters(systemParameters);
|
||||
this.database.AddOrUpdate(systemParameters);
|
||||
|
||||
this.StateChanged?.Invoke(position, meter.FirmwareInRange, FIRMWARE, meter.Firmware);
|
||||
this.StateChanged?.Invoke(position, true, BUILD_INFO, meter.BuildInfo);
|
||||
this.StateChanged?.Invoke(position, meter.ManifacturingTimeInRange, CREATED_ON, $"{meter.ManifacturingTime:yyyy-MM-dd}");
|
||||
this.StateChanged?.Invoke(position, meter.SystemTimeInRange, LAST_UPDATE, $"{meter.SystemTimeDrift} Sek.");
|
||||
this.StateChanged?.Invoke(position, meter.RebootsCount != 0xFF, REBOOTS_CNT, meter.RebootsCount.ToString());
|
||||
this.StateChanged?.Invoke(position, !meter.FactorySealed, SEALED, !meter.FactorySealed ? X : OK);
|
||||
|
||||
if (meter.Initialized)
|
||||
{
|
||||
var inputVolume = this.database.GetInputVolume(meter.Size);
|
||||
var inputVolumResponse = connection.SetInputVolume(inputVolume);
|
||||
meter.Initialized = inputVolumResponse.Succeeded;
|
||||
|
||||
if (meter.Initialized)
|
||||
{
|
||||
meter.AddInputVolume(inputVolume);
|
||||
}
|
||||
}
|
||||
|
||||
this.StateChanged?.Invoke(position, meter.Initialized, ML_PER_PULSE, $"{meter.MlPerPulse:0.00000}");
|
||||
}
|
||||
else // meter or connection not in use
|
||||
{
|
||||
this.StateChanged?.Invoke(position, null, CONNECTED, null);
|
||||
this.StateChanged?.Invoke(position, null, FIRMWARE, null);
|
||||
this.StateChanged?.Invoke(position, null, BUILD_INFO, null);
|
||||
this.StateChanged?.Invoke(position, null, CREATED_ON, null);
|
||||
this.StateChanged?.Invoke(position, null, LAST_UPDATE, null);
|
||||
this.StateChanged?.Invoke(position, null, REBOOTS_CNT, null);
|
||||
this.StateChanged?.Invoke(position, null, SEALED, null);
|
||||
this.StateChanged?.Invoke(position, null, ML_PER_PULSE, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void StartOmniCalibration()
|
||||
{
|
||||
var factory = Task.Factory;
|
||||
var inspectionPoints = this.database.GetInspectionPoints(this.size);
|
||||
|
||||
foreach (var point in inspectionPoints)
|
||||
{
|
||||
this.namedPipeClient.RequestFlowRate(point.FlowRate);
|
||||
|
||||
foreach (var position in this.connections.Keys)
|
||||
{
|
||||
this.tasks[position] = factory.StartNew(() => this.StartOmniCalibration(position, point));
|
||||
}
|
||||
|
||||
factory.ContinueWhenAll(this.tasks.Values.ToArray(), _ => { });
|
||||
|
||||
var volume = this.namedPipeClient.RequestReference();
|
||||
|
||||
foreach (var meter in this.meters.Values)
|
||||
{
|
||||
meter.AddInspectionVolume(point, volume);
|
||||
}
|
||||
}
|
||||
|
||||
foreach(var position in this.connections.Keys)
|
||||
{
|
||||
this.tasks[position] = factory.StartNew(() => this.CalibrateMeter(position));
|
||||
}
|
||||
|
||||
factory.ContinueWhenAll(this.tasks.Values.ToArray(), _ => { });
|
||||
}
|
||||
|
||||
private void StartOmniCalibration(int position, OmniPlusInspectionPoint point)
|
||||
{
|
||||
var header = $"{point.Rotations}/{point.Seconds}R/Sek.";
|
||||
var progress = 0;
|
||||
|
||||
this.StateChanged?.Invoke(position, null, header, $"{progress}%");
|
||||
|
||||
if (this.TryGetOmni(position, out var connection, out var meter))
|
||||
{
|
||||
if (!meter.CanCalibrate)
|
||||
{
|
||||
this.StateChanged?.Invoke(position, false, header, X);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var activationResponse = connection.StartChamberCalibration(point.Rotations, point.Seconds);
|
||||
var calibrationResult = default(ChamberCalibration);
|
||||
|
||||
if (activationResponse.Succeeded)
|
||||
{
|
||||
progress += 10;
|
||||
|
||||
this.StateChanged?.Invoke(position, true, header, $"{progress}%");
|
||||
|
||||
//0x00 indicates that the parameters are valid and test will start 3 seconds after the response.
|
||||
Thread.Sleep(3000);
|
||||
|
||||
progress += 10;
|
||||
this.StateChanged?.Invoke(position, true, header, $"{progress}%");
|
||||
|
||||
// waiting all the secconds to execute test
|
||||
Thread.Sleep(point.Seconds * 1000);
|
||||
|
||||
progress += 70;
|
||||
this.StateChanged?.Invoke(position, true, header, $"{progress}%");
|
||||
|
||||
var delay = (int)(point.Seconds * 0.1) * 1000;
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
calibrationResult = connection.ViewChamberCalibration();
|
||||
|
||||
if (calibrationResult.Succeeded)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
Thread.Sleep(delay);
|
||||
|
||||
progress += 3;
|
||||
this.StateChanged?.Invoke(position, true, header, $"{progress}%");
|
||||
}
|
||||
|
||||
if (calibrationResult?.Succeeded == true)
|
||||
{
|
||||
if (calibrationResult.Control == ChamberCalibration.TEST_COMPLETED)
|
||||
{
|
||||
this.StateChanged?.Invoke(position, true, header, $"100%");
|
||||
}
|
||||
else // calibration timeout or not ready but exceeds the 3s + seconds to run + 0.3%
|
||||
{
|
||||
this.StateChanged?.Invoke(position, false, header, X);
|
||||
}
|
||||
}
|
||||
else // calibration failed
|
||||
{
|
||||
this.StateChanged?.Invoke(position, false, header, "n/a");
|
||||
}
|
||||
}
|
||||
else // activation failed
|
||||
{
|
||||
this.StateChanged?.Invoke(position, false, header, "n/a");
|
||||
}
|
||||
|
||||
meter.AddCalibrationResult(point, calibrationResult);
|
||||
}
|
||||
else // meter or connection not in use
|
||||
{
|
||||
this.StateChanged?.Invoke(position, null, header, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void CalibrateMeter(int position)
|
||||
{
|
||||
if (this.TryGetOmni(position, out var connection, out var meter))
|
||||
{
|
||||
var correctionFactor = meter.CalculateCorrectionFactor();
|
||||
|
||||
this.StateChanged?.Invoke(position, meter.Calibrated, CORRECTION, $"{correctionFactor:0.00}%");
|
||||
}
|
||||
else // meter or connection not in use
|
||||
{
|
||||
this.StateChanged?.Invoke(position, null, CORRECTION, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void FinishOmniInspection()
|
||||
{
|
||||
var factory = Task.Factory;
|
||||
|
||||
foreach (var position in this.connections.Keys)
|
||||
{
|
||||
this.tasks[position] = factory.StartNew(() => this.FinishOmniInspection(position));
|
||||
}
|
||||
}
|
||||
|
||||
private void FinishOmniInspection(int position)
|
||||
{
|
||||
if (this.TryGetOmni(position, out var connection, out var meter))
|
||||
{
|
||||
var configurationParameters = connection.ViewConfigurationParameters();
|
||||
var canContinue = configurationParameters.Succeeded;
|
||||
|
||||
if (canContinue)
|
||||
{
|
||||
var systemTimeReset = connection.SetSystemTime();
|
||||
canContinue = systemTimeReset.Succeeded;
|
||||
}
|
||||
|
||||
if (canContinue)
|
||||
{
|
||||
var registerSettings = connection.ViewRegisterSettings();
|
||||
}
|
||||
}
|
||||
else // meter or connection not in use
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryGetOmni(int position, out OmniPlusConnection connection, out OmniPlusMeter meter)
|
||||
{
|
||||
var connectionFound = this.connections.TryGetValue(position, out connection);
|
||||
var meterFound = this.meters.TryGetValue(position, out meter);
|
||||
|
||||
return connectionFound
|
||||
&& connection != null
|
||||
&& meterFound
|
||||
&& meter != null
|
||||
&& meter.Connected;
|
||||
}
|
||||
|
||||
private void NamedPipeClient_Connected()
|
||||
=> this.available = true;
|
||||
|
||||
private void NamedPipeClient_Disconnected()
|
||||
=> this.available = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using OmniPlus.Features;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace OmniPlus.UI.Inspection
|
||||
{
|
||||
internal class OmniPlusDatabase
|
||||
{
|
||||
private readonly string connectionString;
|
||||
|
||||
public OmniPlusDatabase(string omniDatabase)
|
||||
=> this.connectionString = omniDatabase;
|
||||
|
||||
internal void AddOrUpdate(SystemParameters systemParameters)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
internal InputVolume GetInputVolume(object size)
|
||||
{
|
||||
return new InputVolume();
|
||||
}
|
||||
|
||||
internal IEnumerable<OmniPlusInspectionPoint> GetInspectionPoints(int size)
|
||||
{
|
||||
return new OmniPlusInspectionPoint[] { };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
using OmniPlus.Options;
|
||||
|
||||
using System;
|
||||
|
||||
namespace OmniPlus
|
||||
{
|
||||
internal static class OmniPlusExtensions
|
||||
{
|
||||
public const byte OxOO = 0x00;
|
||||
public const byte OxO1 = 0x01;
|
||||
public const byte OxO2 = 0x02;
|
||||
public const byte OxO3 = 0x03;
|
||||
|
||||
public const byte UNIPRO_START = 0x53;
|
||||
public const byte UNIPRO_PC2REG = 0x57;
|
||||
public const byte UNIPRO_REG2PC = 0x52;
|
||||
public const byte UNIPRO_STOP = 0x0D;
|
||||
public const byte UNIPRO_MIN_LENGTH = 4;
|
||||
|
||||
public const byte UI1236_START = 0x0D;
|
||||
public const byte UI1236_MIN_LENGTH = 5;
|
||||
public const byte UI1236_HED_LENGTH = 3;
|
||||
|
||||
public const byte IRDA_START = 0x9B;
|
||||
public const byte IRDA_PC2REG = 0x33;
|
||||
public const byte IRDA_REG2PC = 0x43;
|
||||
public const byte IRDA_MIN_LENGTH = 5;
|
||||
public const byte IRDA_CRC_LENGTH = 2;
|
||||
|
||||
/// <summary>
|
||||
/// <para>UI1236 structure:</para>
|
||||
/// <para> 0. 0x0D - Start byte</para>
|
||||
/// <para> 1. .... - Length: max. 254</para>
|
||||
/// <para> 2. .... - Control byte</para>
|
||||
/// <para>3+n. .... - Payload</para>
|
||||
/// <para>n-2. .... - Checksum 0 </para>
|
||||
/// <para>n-1. .... - Checksum 1 </para>
|
||||
/// </summary>
|
||||
public static byte[] ToUI1236Message(this byte[] bytes)
|
||||
{
|
||||
var bytesLength = bytes.Length;
|
||||
var payloadLength = bytes.Length + UI1236_MIN_LENGTH;
|
||||
payloadLength += payloadLength % OxO2;
|
||||
|
||||
var index = OxOO;
|
||||
var message = new byte[payloadLength];
|
||||
message[index++] = UI1236_START;
|
||||
message[index++] = (byte)(bytesLength + UI1236_HED_LENGTH);
|
||||
|
||||
OmniControlOptions control = OxOO;
|
||||
control.ReturnResponse = true;
|
||||
message[index++] = control;
|
||||
|
||||
bytes.CopyTo(message, index--);
|
||||
|
||||
var checksum = message.CalculateCheckSUM(message.Length - OxO2);
|
||||
var checksumIndex = bytesLength + UI1236_HED_LENGTH;
|
||||
message[checksumIndex++] = checksum[OxO1];
|
||||
message[checksumIndex++] = checksum[OxOO];
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>UI1236 structure:</para>
|
||||
/// <para> 0. 0x0D - Start byte</para>
|
||||
/// <para> 1. .... - Length: max. 254</para>
|
||||
/// <para> 2. .... - Control byte</para>
|
||||
/// <para>3+n. .... - Payload</para>
|
||||
/// <para>n-2. .... - Checksum 0 </para>
|
||||
/// <para>n-1. .... - Checksum 1 </para>
|
||||
/// </summary>
|
||||
public static byte[] FromUI1236Message(this byte[] bytes)
|
||||
{
|
||||
var payload = new byte[OxOO];
|
||||
|
||||
if (bytes is null)
|
||||
{
|
||||
return payload;
|
||||
}
|
||||
|
||||
var payloadLength = bytes.Length - UI1236_MIN_LENGTH;
|
||||
|
||||
if (payloadLength <= OxOO)
|
||||
{
|
||||
return payload;
|
||||
}
|
||||
|
||||
payload = new byte[payloadLength];
|
||||
|
||||
Array.Copy(bytes, OxO3, payload, OxOO, payloadLength);
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>UniPro structure:</para>
|
||||
/// <para> 0. .... - Start byte</para>
|
||||
/// <para> 1. .... - Direction</para>
|
||||
/// <para> 2. .... - Length</para>
|
||||
/// <para> .. .... - Payload</para>
|
||||
/// <para>n-1. .... - Stop byte</para>
|
||||
/// </summary>
|
||||
public static byte[] ToUniProMessage(this byte[] bytes)
|
||||
{
|
||||
var messageLength = (byte)(bytes.Length + UNIPRO_MIN_LENGTH);
|
||||
var message = new byte[messageLength];
|
||||
var index = OxOO;
|
||||
|
||||
message[index++] = UNIPRO_START;
|
||||
message[index++] = UNIPRO_PC2REG;
|
||||
message[index++] = messageLength;
|
||||
message[messageLength - OxO1] = UNIPRO_STOP;
|
||||
|
||||
bytes.CopyTo(message, index);
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>UniPro structure:</para>
|
||||
/// <para> 0. .... - Start byte</para>
|
||||
/// <para> 1. .... - Direction</para>
|
||||
/// <para> 2. .... - Length</para>
|
||||
/// <para> .. .... - Payload</para>
|
||||
/// <para>n-1. .... - Stop byte</para>
|
||||
/// </summary>
|
||||
public static byte[] FromUniProMessage(this byte[] bytes)
|
||||
{
|
||||
var payload = new byte[OxOO];
|
||||
|
||||
if (bytes is null)
|
||||
{
|
||||
return payload;
|
||||
}
|
||||
|
||||
var payloadLength = bytes.Length - UNIPRO_MIN_LENGTH;
|
||||
|
||||
if (payloadLength <= OxOO)
|
||||
{
|
||||
return payload;
|
||||
}
|
||||
|
||||
payload = new byte[payloadLength];
|
||||
|
||||
Array.Copy(bytes, OxO3, payload, OxOO, payloadLength);
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Irda structure:</para>
|
||||
/// <para> 0. .... - Start byte</para>
|
||||
/// <para> 1. .... - Direction</para>
|
||||
/// <para> 2. .... - Length / 2</para>
|
||||
/// <para>3+n. .... - Payload</para>
|
||||
/// <para>n-3. .... - aligned byte</para>
|
||||
/// <para>n-2. .... - CRC16 0 </para>
|
||||
/// <para>n-1. .... - CRC16 1 </para>
|
||||
/// </summary>
|
||||
public static byte[] ToIrdaMessage(this byte[] bytes)
|
||||
{
|
||||
var payloadLength = bytes.Length;
|
||||
payloadLength += payloadLength % OxO2;
|
||||
|
||||
var index = OxOO;
|
||||
var message = new byte[payloadLength + IRDA_MIN_LENGTH];
|
||||
message[index++] = IRDA_START;
|
||||
message[index++] = IRDA_PC2REG;
|
||||
message[index++] = (byte)(payloadLength / OxO2);
|
||||
|
||||
bytes.CopyTo(message, index);
|
||||
|
||||
var messageLength = message.Length;
|
||||
var crc16 = message.CalculateCRC8408(1, messageLength - IRDA_CRC_LENGTH);
|
||||
message[messageLength - OxO2] = crc16[OxOO];
|
||||
message[messageLength - OxO1] = crc16[OxO1];
|
||||
|
||||
return message;
|
||||
}
|
||||
/// <summary>
|
||||
/// <para>Irda structure:</para>
|
||||
/// <para> 0. .... - Start byte</para>
|
||||
/// <para> 1. .... - Direction</para>
|
||||
/// <para> 2. .... - Length / 2</para>
|
||||
/// <para>3+n. .... - Payload</para>
|
||||
/// <para>n-3. .... - aligned byte</para>
|
||||
/// <para>n-2. .... - CRC16 0 </para>
|
||||
/// <para>n-1. .... - CRC16 1 </para>
|
||||
/// </summary>
|
||||
public static byte[] FromIrdaMessage(this byte[] bytes)
|
||||
{
|
||||
var payload = new byte[OxOO];
|
||||
|
||||
if (bytes is null)
|
||||
{
|
||||
return payload;
|
||||
}
|
||||
|
||||
var payloadLength = bytes.Length - IRDA_MIN_LENGTH;
|
||||
|
||||
if (payloadLength <= OxOO)
|
||||
{
|
||||
return payload;
|
||||
}
|
||||
|
||||
payload = new byte[payloadLength];
|
||||
|
||||
Array.Copy(bytes, OxO3, payload, OxOO, payloadLength);
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
public static byte[] CalculateCRC8408(this byte[] message, int start, int length)
|
||||
{
|
||||
var crc = 0xFFFF;
|
||||
|
||||
for (int x = start; x < length; x++)
|
||||
{
|
||||
crc ^= 0x00FF & message[x];
|
||||
|
||||
for (int i = 8; i > OxOO; i--)
|
||||
{
|
||||
if ((crc & 0x0001) != OxOO)
|
||||
{
|
||||
crc = (crc >> OxO1) ^ 0x8408;
|
||||
}
|
||||
else
|
||||
{
|
||||
crc >>= OxO1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
crc = ~crc;
|
||||
|
||||
return BitConverter.GetBytes(crc);
|
||||
}
|
||||
|
||||
public static byte[] CalculateCheckSUM(this byte[] bytes, int length)
|
||||
{
|
||||
ushort checksum = OxOO;
|
||||
|
||||
for (int i = OxOO; i < length; i++)
|
||||
{
|
||||
checksum += bytes[i];
|
||||
}
|
||||
|
||||
return BitConverter.GetBytes(checksum);
|
||||
}
|
||||
|
||||
public static void Clear(this byte[] bytes)
|
||||
{
|
||||
var length = bytes.Length;
|
||||
|
||||
for (int i = OxOO; i < length; i++)
|
||||
{
|
||||
bytes[i] = OxOO;
|
||||
}
|
||||
}
|
||||
|
||||
public static string ToASCIIString(this byte[] bytes, int start, int end)
|
||||
{
|
||||
var asciiString = string.Empty;
|
||||
|
||||
while (start <= end)
|
||||
{
|
||||
asciiString += (char)bytes[start];
|
||||
start++;
|
||||
}
|
||||
|
||||
return asciiString.Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
namespace OmniPlus.UI.Inspection
|
||||
{
|
||||
internal class OmniPlusInspectionHeaders
|
||||
{
|
||||
public const string COMS_CONFIG = "COMS";
|
||||
|
||||
public const string SLOT_NR = "Einbauplatz";
|
||||
public const string CONNECTED = "Verbunden";
|
||||
public const string FIRMWARE = "Firmware";
|
||||
public const string CREATED_ON = "Herstellungsdatum";
|
||||
public const string LAST_UPDATE = "Letzte Aktualisierung";
|
||||
public const string REBOOTS_CNT = "Neustarts";
|
||||
public const string SEALED = "Versiegelt";
|
||||
public const string BUILD_INFO = "Build-Info";
|
||||
public const string ML_PER_PULSE = "ml/Puls";
|
||||
public const string CORRECTION = "Korrekturfaktor";
|
||||
//public const string REBOOTS_CNT = "Neustarts";
|
||||
//public const string REBOOTS_CNT = "Neustarts";
|
||||
|
||||
public const string OK = "✓";
|
||||
public const string X = "✘";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace OmniPlus.UI.Inspection
|
||||
{
|
||||
using System;
|
||||
|
||||
internal struct OmniPlusInspectionPoint : IComparable<OmniPlusInspectionPoint>
|
||||
{
|
||||
public double FlowRate { get; set; }
|
||||
|
||||
public ushort Rotations { get; set; }
|
||||
|
||||
public ushort Seconds { get; set; }
|
||||
|
||||
public int CompareTo(OmniPlusInspectionPoint other)
|
||||
=> this.Rotations.CompareTo(other.Rotations);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
using OmniPlus.Features;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace OmniPlus.UI.Inspection
|
||||
{
|
||||
internal class OmniPlusMeter
|
||||
{
|
||||
private readonly IDictionary<OmniPlusInspectionPoint, double> inspectionPoints;
|
||||
private readonly IDictionary<OmniPlusInspectionPoint, ChamberCalibration> calibrationResults;
|
||||
|
||||
private InputVolume inputVolume;
|
||||
private SystemParameters systemParameters;
|
||||
|
||||
public OmniPlusMeter(bool connected = false)
|
||||
{
|
||||
this.Connected = connected;
|
||||
this.inputVolume = new InputVolume();
|
||||
this.systemParameters = new SystemParameters();
|
||||
this.inspectionPoints = new Dictionary<OmniPlusInspectionPoint, double>();
|
||||
this.calibrationResults = new Dictionary<OmniPlusInspectionPoint, ChamberCalibration>();
|
||||
}
|
||||
|
||||
public bool Connected { get; set; }
|
||||
|
||||
public bool Initialized { get; set; }
|
||||
|
||||
public bool CanCalibrate { get; set; } = true;
|
||||
|
||||
public bool Calibrated { get; private set; }
|
||||
|
||||
public byte Size
|
||||
{
|
||||
get => this.inputVolume.Size;
|
||||
set => this.inputVolume.Size = value;
|
||||
}
|
||||
|
||||
public byte Range
|
||||
{
|
||||
get => this.inputVolume.Range;
|
||||
set => this.inputVolume.Range = value;
|
||||
}
|
||||
|
||||
public ushort IntPart
|
||||
{
|
||||
get => this.inputVolume.IntPart;
|
||||
set => this.inputVolume.IntPart = value;
|
||||
}
|
||||
|
||||
public ushort Fraction
|
||||
{
|
||||
get => this.inputVolume.Fraction;
|
||||
set => this.inputVolume.Fraction = value;
|
||||
}
|
||||
|
||||
public string BuildInfo
|
||||
{
|
||||
get => this.systemParameters.BuildInfo.Value.Trim('\0');
|
||||
set => this.systemParameters.BuildInfo.Value = value;
|
||||
}
|
||||
|
||||
public DateTime ManifacturingTime
|
||||
{
|
||||
get => this.systemParameters.ManifacturingTime.DateTime;
|
||||
set => this.systemParameters.ManifacturingTime.DateTime = value;
|
||||
}
|
||||
|
||||
public bool ManifacturingTimeInRange { get; private set; }
|
||||
|
||||
public DateTime SystemTime
|
||||
{
|
||||
get => this.systemParameters.SystemTime.DateTime;
|
||||
set => this.systemParameters.SystemTime.DateTime = value;
|
||||
}
|
||||
|
||||
public int SystemTimeDrift { get; private set; }
|
||||
|
||||
public bool SystemTimeInRange { get; private set; }
|
||||
|
||||
public byte RebootsCount
|
||||
{
|
||||
get => this.systemParameters.RebootsCount;
|
||||
set => this.systemParameters.RebootsCount = value;
|
||||
}
|
||||
|
||||
public string Firmware
|
||||
{
|
||||
get => this.systemParameters.VersionAndType.Value.Trim('\0');
|
||||
set => this.systemParameters.VersionAndType.Value = value;
|
||||
}
|
||||
|
||||
public bool FirmwareInRange { get; private set; }
|
||||
|
||||
public double Volts
|
||||
{
|
||||
get => Math.Max(this.systemParameters.BatteryVoltage.Volts, (ushort)1) / 1000.0;
|
||||
set => this.systemParameters.BatteryVoltage.Volts = (ushort)(value * 1000);
|
||||
}
|
||||
|
||||
public bool VoltsInRange => 3.19 <= this.Volts && this.Volts <= 3.75;
|
||||
|
||||
public uint ADCCounts
|
||||
{
|
||||
get => this.systemParameters.BatteryVoltage.ADCCount;
|
||||
set => this.systemParameters.BatteryVoltage.ADCCount = value;
|
||||
}
|
||||
|
||||
public bool FactorySealed
|
||||
{
|
||||
get => this.systemParameters.FactorySealed;
|
||||
set => this.systemParameters.FactorySealed = value;
|
||||
}
|
||||
|
||||
public double MlPerPulse { get; private set; }
|
||||
|
||||
internal void AddInputVolume(InputVolume inputVolume)
|
||||
{
|
||||
if (inputVolume != null)
|
||||
{
|
||||
this.inputVolume = inputVolume;
|
||||
|
||||
if (this.inputVolume.Fraction > 0)
|
||||
{
|
||||
this.MlPerPulse = this.inputVolume.Fraction / ushort.MaxValue * this.inputVolume.Factor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void AddSystemParameters(SystemParameters systemParameters)
|
||||
{
|
||||
if (systemParameters != null)
|
||||
{
|
||||
this.systemParameters = systemParameters;
|
||||
|
||||
this.SystemTimeDrift = (DateTime.UtcNow - this.SystemTime).Seconds;
|
||||
this.SystemTimeInRange = Math.Abs(this.SystemTimeDrift) <= 700;
|
||||
this.ManifacturingTimeInRange = Math.Abs((DateTime.UtcNow - this.ManifacturingTime).Days) <= 365;
|
||||
|
||||
var firmwareTokens = this.Firmware
|
||||
?.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
?? new string[2];
|
||||
var versionString = firmwareTokens[0]?.Substring(1) ?? string.Empty;
|
||||
var versionParsed = double.TryParse(versionString, out var version);
|
||||
var typeString = firmwareTokens[1]?.ToUpper() ?? string.Empty;
|
||||
var isOmni = typeString == "OMNI-1" || typeString == "OMNI-2";
|
||||
|
||||
this.FirmwareInRange = versionParsed && version >= 1.23 && isOmni;
|
||||
}
|
||||
}
|
||||
|
||||
internal void AddCalibrationResult(OmniPlusInspectionPoint point, ChamberCalibration result)
|
||||
{
|
||||
this.calibrationResults[point] = result;
|
||||
|
||||
if (this.CanCalibrate)
|
||||
{
|
||||
this.CanCalibrate
|
||||
= result != null
|
||||
&& result.Succeeded
|
||||
&& result.Control == ChamberCalibration.TEST_COMPLETED;
|
||||
}
|
||||
}
|
||||
|
||||
internal void AddInspectionVolume(OmniPlusInspectionPoint point, double volume)
|
||||
=> this.inspectionPoints[point] = volume;
|
||||
|
||||
internal double CalculateCorrectionFactor()
|
||||
{
|
||||
// TODO: calculate correction factor.
|
||||
return double.MinValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
namespace OmniPlus.UI.Inspection
|
||||
{
|
||||
public delegate void OmniPlusStateChangedEventHandler(int column, bool? succeeded, string header, string value);
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
namespace OmniPlus.Options
|
||||
{
|
||||
internal class OmniControlOptions
|
||||
{
|
||||
private readonly byte[] bits;
|
||||
|
||||
private OmniControlOptions(byte[] bits)
|
||||
=> this.bits = bits;
|
||||
|
||||
public bool E0Encryption => this.bits[0] == 1;
|
||||
|
||||
public bool E1Encryption => this.bits[1] == 1;
|
||||
|
||||
public bool E2Encryption => this.bits[2] == 1;
|
||||
|
||||
public bool ReturnResponse
|
||||
{
|
||||
get => this.bits[3] == 1;
|
||||
set => this.bits[3] = (byte)(value ? 1 : 0);
|
||||
}
|
||||
|
||||
public bool NFNetwork => this.bits[4] == 1;
|
||||
|
||||
public bool MultipleCommands
|
||||
{
|
||||
get => this.bits[5] == 1;
|
||||
set => this.bits[5] = (byte)(value ? 1 : 0);
|
||||
}
|
||||
|
||||
public bool LongControl
|
||||
{
|
||||
get => this.bits[6] == 1;
|
||||
set => this.bits[6] = (byte)(value ? 1 : 0);
|
||||
}
|
||||
|
||||
public bool MultipleFrames
|
||||
{
|
||||
get => this.bits[7] == 1;
|
||||
set => this.bits[7] = (byte)(value ? 1 : 0);
|
||||
}
|
||||
|
||||
public static implicit operator OmniControlOptions(byte controlByte)
|
||||
{
|
||||
var bits = new byte[8];
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
bits[i] = (byte)((controlByte >> i) & 1);
|
||||
}
|
||||
|
||||
return new OmniControlOptions(bits);
|
||||
}
|
||||
|
||||
public static implicit operator byte (OmniControlOptions control)
|
||||
{
|
||||
var @byte = default(byte);
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
@byte ^= (byte)(control.bits[i] << i);
|
||||
}
|
||||
|
||||
return @byte;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using OmniPlus.Enums;
|
||||
|
||||
using System.IO.Ports;
|
||||
|
||||
namespace OmniPlus.Options
|
||||
{
|
||||
public class SerialPortOptions
|
||||
{
|
||||
public SerialPortOptions()
|
||||
{
|
||||
this.BaudRate = BaudRate.K4800;
|
||||
this.DataBits = DataBits.X8;
|
||||
this.Parity = Parity.None;
|
||||
this.StopBits = StopBits.One;
|
||||
this.Handshake = Handshake.None;
|
||||
}
|
||||
|
||||
public string PortName { get; set; }
|
||||
|
||||
public BaudRate BaudRate { get; set; }
|
||||
|
||||
public Parity Parity { get; set; }
|
||||
|
||||
public DataBits DataBits { get; set; }
|
||||
|
||||
public StopBits StopBits { get; set; }
|
||||
|
||||
public Handshake Handshake { get; set; }
|
||||
|
||||
public bool RtsEnabled { get; set; }
|
||||
|
||||
public bool DtrEnabled { get; set; }
|
||||
|
||||
public string LogFile { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("OmniPlus")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("OmniPlus")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2023")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("f8d84b9a-0fcf-4083-b067-8c39b7314152")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
Reference in New Issue
Block a user