|
|
|
@ -0,0 +1,250 @@ |
|
|
|
|
using System; |
|
|
|
|
using System.Collections.Generic; |
|
|
|
|
using System.Collections.ObjectModel; |
|
|
|
|
using System.Linq; |
|
|
|
|
using System.Text; |
|
|
|
|
using System.Threading.Tasks; |
|
|
|
|
using System.Timers; |
|
|
|
|
using EasyModbus; |
|
|
|
|
|
|
|
|
|
namespace ModbusTester.Model |
|
|
|
|
{ |
|
|
|
|
public class ModbusModel |
|
|
|
|
{ |
|
|
|
|
private readonly int COIL_ADDR_DATA_SAMPLING = 1 - 1; |
|
|
|
|
private readonly int COIL_ADDR_DATA_AVAILABLE = 2 - 1; |
|
|
|
|
private readonly int COIL_ADDR_DATA_CLEAR = 3 - 1; |
|
|
|
|
|
|
|
|
|
private readonly int HOLDING_RESISTER_SAMPLING_INTERVAL = 3 - 1; |
|
|
|
|
private readonly int HOLDING_RESISTER_TARE_TIME = 4 - 1; |
|
|
|
|
private readonly int HOLDING_RESISTER_REPEAT_COUNT = 5 - 1; |
|
|
|
|
|
|
|
|
|
private readonly int INPUT_RESISTER_CONFIG_FLOW_RATE = 13 - 1; // length: 2 |
|
|
|
|
private readonly int INPUT_RESISTER_FLOW_RATE = 222 - 1; // length: 2 |
|
|
|
|
private readonly int INPUT_RESISTER_ADDR_NUM_OF_CHANNEL = 227 - 1; // length: 1 |
|
|
|
|
private readonly int INPUT_RESISTER_CHANNEL_SIZE_START = 17 - 1; // length: 2 for each |
|
|
|
|
private readonly int INPUT_RESISTER_CHANNEL_START = 229 - 1; // length: 2 for each |
|
|
|
|
|
|
|
|
|
private ModbusClient _master; |
|
|
|
|
|
|
|
|
|
private Timer _timer; |
|
|
|
|
|
|
|
|
|
public string IP { get; set; } |
|
|
|
|
public int Port { get; set; } |
|
|
|
|
|
|
|
|
|
public int Interval { get; set; } |
|
|
|
|
|
|
|
|
|
public bool? IsConnected { get { return _master?.Connected; } } |
|
|
|
|
|
|
|
|
|
public event EventHandler<bool> OnConnectionChanged; |
|
|
|
|
public event EventHandler<byte[]> OnSendDataChanged; |
|
|
|
|
public event EventHandler<byte[]> OnReceiveDataChanged; |
|
|
|
|
public event EventHandler<DataModel> OnDataRead; |
|
|
|
|
|
|
|
|
|
public ModbusModel() |
|
|
|
|
{ |
|
|
|
|
Port = 502; |
|
|
|
|
|
|
|
|
|
_master = new ModbusClient(); |
|
|
|
|
_master.ReceiveDataChanged += _master_ReceiveDataChanged; |
|
|
|
|
_master.SendDataChanged += _master_SendDataChanged; |
|
|
|
|
_master.ConnectedChanged += _master_ConnectedChanged; |
|
|
|
|
|
|
|
|
|
_timer = new Timer(); |
|
|
|
|
_timer.Interval = 1000; |
|
|
|
|
_timer.Elapsed += _timer_Elapsed; |
|
|
|
|
_timer.Stop(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void _timer_Elapsed(object sender, ElapsedEventArgs e) |
|
|
|
|
{ |
|
|
|
|
if (_master == null || !_master.Connected) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
// data available check |
|
|
|
|
bool available = _master.ReadCoils(COIL_ADDR_DATA_AVAILABLE, 1)[0]; |
|
|
|
|
if (!available) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
// get data |
|
|
|
|
ReadData(); |
|
|
|
|
|
|
|
|
|
// pop queued data |
|
|
|
|
_master.WriteSingleCoil(COIL_ADDR_DATA_AVAILABLE, false); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void _master_ConnectedChanged(object sender) |
|
|
|
|
{ |
|
|
|
|
if (OnConnectionChanged != null) |
|
|
|
|
OnConnectionChanged(this, _master.Connected); |
|
|
|
|
|
|
|
|
|
if (_master.Connected) |
|
|
|
|
_timer.Start(); |
|
|
|
|
else |
|
|
|
|
_timer.Stop(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void _master_SendDataChanged(object sender) |
|
|
|
|
{ |
|
|
|
|
if (OnSendDataChanged != null) |
|
|
|
|
OnSendDataChanged(this, _master.sendData); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void _master_ReceiveDataChanged(object sender) |
|
|
|
|
{ |
|
|
|
|
if (OnReceiveDataChanged != null) |
|
|
|
|
OnReceiveDataChanged(this, _master.receiveData); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public void Connect() |
|
|
|
|
{ |
|
|
|
|
Disconnect(); |
|
|
|
|
|
|
|
|
|
_master.IPAddress = IP; |
|
|
|
|
_master.Port = Port; |
|
|
|
|
_master.UnitIdentifier = 1; |
|
|
|
|
_master.Connect(); |
|
|
|
|
|
|
|
|
|
// clear queue |
|
|
|
|
_master.WriteSingleCoil(COIL_ADDR_DATA_CLEAR, true); |
|
|
|
|
|
|
|
|
|
// write interval |
|
|
|
|
_master.WriteSingleRegister(HOLDING_RESISTER_SAMPLING_INTERVAL, Interval); |
|
|
|
|
|
|
|
|
|
// write tare |
|
|
|
|
_master.WriteSingleRegister(HOLDING_RESISTER_TARE_TIME, 0); |
|
|
|
|
|
|
|
|
|
// set repeat count |
|
|
|
|
_master.WriteSingleRegister(HOLDING_RESISTER_REPEAT_COUNT, 0); |
|
|
|
|
|
|
|
|
|
// read basic information |
|
|
|
|
ReadData(); |
|
|
|
|
|
|
|
|
|
// Sampling Start |
|
|
|
|
StartSampling(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public void Disconnect() |
|
|
|
|
{ |
|
|
|
|
if (!_master.Connected) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
StopSampling(); |
|
|
|
|
|
|
|
|
|
_master.Disconnect(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public void StartSampling() |
|
|
|
|
{ |
|
|
|
|
if (_master == null || !_master.Connected) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
// coil check |
|
|
|
|
bool samping = _master.ReadCoils(COIL_ADDR_DATA_SAMPLING, 1)[0]; |
|
|
|
|
if (!samping) |
|
|
|
|
_master.WriteSingleCoil(COIL_ADDR_DATA_SAMPLING, true); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public void StopSampling() |
|
|
|
|
{ |
|
|
|
|
if (_master == null || !_master.Connected) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
// coil check |
|
|
|
|
bool samping = _master.ReadCoils(COIL_ADDR_DATA_SAMPLING, 1)[0]; |
|
|
|
|
if (samping) |
|
|
|
|
_master.WriteSingleCoil(COIL_ADDR_DATA_SAMPLING, false); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public DataModel ReadData() |
|
|
|
|
{ |
|
|
|
|
if (_master == null || !_master.Connected) |
|
|
|
|
return null; |
|
|
|
|
|
|
|
|
|
// interval |
|
|
|
|
int interval = _master.ReadHoldingRegisters(HOLDING_RESISTER_SAMPLING_INTERVAL, 1)[0]; |
|
|
|
|
|
|
|
|
|
// config flowrate |
|
|
|
|
int[] configFlowratePacket = _master.ReadInputRegisters(INPUT_RESISTER_CONFIG_FLOW_RATE, 2); |
|
|
|
|
float configFlowrate = ConvertHighLowToFloat(configFlowratePacket)[0]; |
|
|
|
|
|
|
|
|
|
// actual flowrate |
|
|
|
|
int[] acturalFlowratePacket = _master.ReadInputRegisters(INPUT_RESISTER_FLOW_RATE, 2); |
|
|
|
|
float acturalFlowrate = ConvertHighLowToFloat(acturalFlowratePacket)[0]; |
|
|
|
|
|
|
|
|
|
// channel data |
|
|
|
|
DateTime now = DateTime.Now; |
|
|
|
|
int channelLength = _master.ReadInputRegisters(INPUT_RESISTER_ADDR_NUM_OF_CHANNEL, 1)[0]; |
|
|
|
|
int[] channetSizePacket = _master.ReadInputRegisters(INPUT_RESISTER_CHANNEL_SIZE_START, channelLength * 2); |
|
|
|
|
int[] channelDataPacket = _master.ReadInputRegisters(INPUT_RESISTER_CHANNEL_START, channelLength * 2); |
|
|
|
|
|
|
|
|
|
// channel data converting |
|
|
|
|
int[] channelSizeData = ConvertHighLowToInt(channetSizePacket); |
|
|
|
|
int[] channelData = ConvertHighLowToInt(channelDataPacket); |
|
|
|
|
PMSChannelData[] data = new PMSChannelData[channelData.Length]; |
|
|
|
|
for (int i = 0; i < channelData.Length; i++) |
|
|
|
|
{ |
|
|
|
|
data[i] = new PMSChannelData() |
|
|
|
|
{ |
|
|
|
|
Name = $"Ch{i + 1} ({channelSizeData[i]})", |
|
|
|
|
TimeStamp = now, |
|
|
|
|
CummulativeRaw = channelData[i] |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
DataModel read = new DataModel() |
|
|
|
|
{ |
|
|
|
|
IP = IP, |
|
|
|
|
Interval = interval, |
|
|
|
|
ConfigFlowrate = configFlowrate, |
|
|
|
|
ActualFlowrate = acturalFlowrate, |
|
|
|
|
NumberOfChannel = channelLength, |
|
|
|
|
Data = new ObservableCollection<PMSChannelData>(data) |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (OnDataRead != null) |
|
|
|
|
OnDataRead(this, read); |
|
|
|
|
|
|
|
|
|
return read; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private float[] ConvertHighLowToFloat(int[] input) |
|
|
|
|
{ |
|
|
|
|
if (input.Length % 2 != 0) |
|
|
|
|
throw new ArgumentException("Input array is not even."); |
|
|
|
|
|
|
|
|
|
float[] output = new float[input.Length / 2]; |
|
|
|
|
for (int i = 0; i < input.Length; i += 2) |
|
|
|
|
{ |
|
|
|
|
ushort high = (ushort)input[i]; |
|
|
|
|
ushort low = (ushort)input[i + 1]; |
|
|
|
|
// big-endian to little-endian |
|
|
|
|
byte[] bytes = BitConverter.GetBytes(low).Concat(BitConverter.GetBytes(high)).ToArray(); |
|
|
|
|
float convertedValue = BitConverter.ToSingle(bytes, 0); |
|
|
|
|
output[i / 2] = convertedValue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return output; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private int[] ConvertHighLowToInt(int[] input) |
|
|
|
|
{ |
|
|
|
|
if (input.Length % 2 != 0) |
|
|
|
|
throw new ArgumentException("Input array is not even."); |
|
|
|
|
|
|
|
|
|
int[] output = new int[input.Length / 2]; |
|
|
|
|
for (int i = 0; i < input.Length; i += 2) |
|
|
|
|
{ |
|
|
|
|
ushort high = (ushort)input[i]; |
|
|
|
|
ushort low = (ushort)input[i + 1]; |
|
|
|
|
// big-endian to little-endian |
|
|
|
|
byte[] bytes = BitConverter.GetBytes(low).Concat(BitConverter.GetBytes(high)).ToArray(); |
|
|
|
|
int convertedValue = BitConverter.ToInt32(bytes, 0); |
|
|
|
|
output[i / 2] = convertedValue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return output; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |