初始化上传
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
|
||||
namespace SharpModbus
|
||||
{
|
||||
public class ModbusF01ReadCoils : IModbusCommand
|
||||
{
|
||||
private readonly byte slave;
|
||||
private readonly ushort address;
|
||||
private readonly ushort count;
|
||||
|
||||
public byte Code { get { return 1; } }
|
||||
public byte Slave { get { return slave; } }
|
||||
public ushort Address { get { return address; } }
|
||||
public ushort Count { get { return count; } }
|
||||
public int RequestLength { get { return 6; } }
|
||||
public int ResponseLength { get { return 3 + ModbusUtils.BytesForBools(count); } }
|
||||
|
||||
public ModbusF01ReadCoils(byte slave, ushort address, ushort count)
|
||||
{
|
||||
this.slave = slave;
|
||||
this.address = address;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public void FillRequest(byte[] request, int offset)
|
||||
{
|
||||
request[offset + 0] = slave;
|
||||
request[offset + 1] = 1;
|
||||
request[offset + 2] = ModbusUtils.High(address);
|
||||
request[offset + 3] = ModbusUtils.Low(address);
|
||||
request[offset + 4] = ModbusUtils.High(count);
|
||||
request[offset + 5] = ModbusUtils.Low(count);
|
||||
}
|
||||
|
||||
public object ParseResponse(byte[] response, int offset)
|
||||
{
|
||||
var bytes = ModbusUtils.BytesForBools(count);
|
||||
Tools.AssertEqual(response[offset + 0], slave, "Slave mismatch got {0} expected {1}");
|
||||
Tools.AssertEqual(response[offset + 1], 1, "Function mismatch got {0} expected {1}");
|
||||
Tools.AssertEqual(response[offset + 2], bytes, "Bytes mismatch got {0} expected {1}");
|
||||
return ModbusUtils.DecodeBools(response, offset + 3, count);
|
||||
}
|
||||
|
||||
public object ApplyTo(IModbusModel model)
|
||||
{
|
||||
return model.getDOs(slave, address, count);
|
||||
}
|
||||
|
||||
public void FillResponse(byte[] response, int offset, object value)
|
||||
{
|
||||
var bytes = ModbusUtils.BytesForBools(count);
|
||||
response[offset + 0] = slave;
|
||||
response[offset + 1] = 1;
|
||||
response[offset + 2] = bytes;
|
||||
var data = ModbusUtils.EncodeBools(value as bool[]);
|
||||
ModbusUtils.Copy(data, 0, response, offset + 3, bytes);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("[ModbusF01ReadCoils Slave={0}, Address={1}, Count={2}]", slave, address, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
|
||||
namespace SharpModbus
|
||||
{
|
||||
public class ModbusF02ReadInputs : IModbusCommand
|
||||
{
|
||||
private readonly byte slave;
|
||||
private readonly ushort address;
|
||||
private readonly ushort count;
|
||||
|
||||
public byte Code { get { return 2; } }
|
||||
public byte Slave { get { return slave; } }
|
||||
public ushort Address { get { return address; } }
|
||||
public ushort Count { get { return count; } }
|
||||
public int RequestLength { get { return 6; } }
|
||||
public int ResponseLength { get { return 3 + ModbusUtils.BytesForBools(count); } }
|
||||
|
||||
public ModbusF02ReadInputs(byte slave, ushort address, ushort count)
|
||||
{
|
||||
this.slave = slave;
|
||||
this.address = address;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public void FillRequest(byte[] request, int offset)
|
||||
{
|
||||
request[offset + 0] = slave;
|
||||
request[offset + 1] = 2;
|
||||
request[offset + 2] = ModbusUtils.High(address);
|
||||
request[offset + 3] = ModbusUtils.Low(address);
|
||||
request[offset + 4] = ModbusUtils.High(count);
|
||||
request[offset + 5] = ModbusUtils.Low(count);
|
||||
}
|
||||
|
||||
public object ParseResponse(byte[] response, int offset)
|
||||
{
|
||||
var bytes = ModbusUtils.BytesForBools(count);
|
||||
Tools.AssertEqual(response[offset + 0], slave, "Slave mismatch got {0} expected {1}");
|
||||
Tools.AssertEqual(response[offset + 1], 2, "Function mismatch got {0} expected {1}");
|
||||
Tools.AssertEqual(response[offset + 2], bytes, "Bytes mismatch got {0} expected {1}");
|
||||
return ModbusUtils.DecodeBools(response, offset + 3, count);
|
||||
}
|
||||
|
||||
public object ApplyTo(IModbusModel model)
|
||||
{
|
||||
return model.getDIs(slave, address, count);
|
||||
}
|
||||
|
||||
public void FillResponse(byte[] response, int offset, object value)
|
||||
{
|
||||
var bytes = ModbusUtils.BytesForBools(count);
|
||||
response[offset + 0] = slave;
|
||||
response[offset + 1] = 2;
|
||||
response[offset + 2] = bytes;
|
||||
var data = ModbusUtils.EncodeBools(value as bool[]);
|
||||
ModbusUtils.Copy(data, 0, response, offset + 3, bytes);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("[ModbusF02ReadInputs Slave={0}, Address={1}, Count={2}]", slave, address, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
|
||||
namespace SharpModbus
|
||||
{
|
||||
public class ModbusF03ReadHoldingRegisters : IModbusCommand
|
||||
{
|
||||
private readonly byte slave;
|
||||
private readonly ushort address;
|
||||
private readonly ushort count;
|
||||
|
||||
public byte Code { get { return 3; } }
|
||||
public byte Slave { get { return slave; } }
|
||||
public ushort Address { get { return address; } }
|
||||
public ushort Count { get { return count; } }
|
||||
public int RequestLength { get { return 6; } }
|
||||
public int ResponseLength { get { return 3 + ModbusUtils.BytesForWords(count); } }
|
||||
|
||||
public ModbusF03ReadHoldingRegisters(byte slave, ushort address, ushort count)
|
||||
{
|
||||
this.slave = slave;
|
||||
this.address = address;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public void FillRequest(byte[] request, int offset)
|
||||
{
|
||||
request[offset + 0] = slave;
|
||||
request[offset + 1] = 3;
|
||||
request[offset + 2] = ModbusUtils.High(address);
|
||||
request[offset + 3] = ModbusUtils.Low(address);
|
||||
request[offset + 4] = ModbusUtils.High(count);
|
||||
request[offset + 5] = ModbusUtils.Low(count);
|
||||
}
|
||||
|
||||
public object ParseResponse(byte[] response, int offset)
|
||||
{
|
||||
var bytes = ModbusUtils.BytesForWords(count);
|
||||
Tools.AssertEqual(response[offset + 0], slave, "Slave mismatch got {0} expected {1}");
|
||||
Tools.AssertEqual(response[offset + 1], 3, "Function mismatch got {0} expected {1}");
|
||||
Tools.AssertEqual(response[offset + 2], bytes, "Bytes mismatch got {0} expected {1}");
|
||||
return ModbusUtils.DecodeWords(response, offset + 3, count);
|
||||
}
|
||||
|
||||
public object ApplyTo(IModbusModel model)
|
||||
{
|
||||
return model.getWOs(slave, address, count);
|
||||
}
|
||||
|
||||
public void FillResponse(byte[] response, int offset, object value)
|
||||
{
|
||||
var bytes = ModbusUtils.BytesForWords(count);
|
||||
response[offset + 0] = slave;
|
||||
response[offset + 1] = 3;
|
||||
response[offset + 2] = bytes;
|
||||
var data = ModbusUtils.EncodeWords((ushort[])value);
|
||||
ModbusUtils.Copy(data, 0, response, offset + 3, bytes);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("[ModbusF03ReadHoldingRegisters Slave={0}, Address={1}, Count={2}]", slave, address, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
|
||||
namespace SharpModbus
|
||||
{
|
||||
public class ModbusF04ReadInputRegisters : IModbusCommand
|
||||
{
|
||||
private readonly byte slave;
|
||||
private readonly ushort address;
|
||||
private readonly ushort count;
|
||||
|
||||
public byte Code { get { return 4; } }
|
||||
public byte Slave { get { return slave; } }
|
||||
public ushort Address { get { return address; } }
|
||||
public ushort Count { get { return count; } }
|
||||
public int RequestLength { get { return 6; } }
|
||||
public int ResponseLength { get { return 3 + ModbusUtils.BytesForWords(count); } }
|
||||
|
||||
public ModbusF04ReadInputRegisters(byte slave, ushort address, ushort count)
|
||||
{
|
||||
this.slave = slave;
|
||||
this.address = address;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public void FillRequest(byte[] request, int offset)
|
||||
{
|
||||
request[offset + 0] = slave;
|
||||
request[offset + 1] = 4;
|
||||
request[offset + 2] = ModbusUtils.High(address);
|
||||
request[offset + 3] = ModbusUtils.Low(address);
|
||||
request[offset + 4] = ModbusUtils.High(count);
|
||||
request[offset + 5] = ModbusUtils.Low(count);
|
||||
}
|
||||
|
||||
public object ParseResponse(byte[] response, int offset)
|
||||
{
|
||||
var bytes = ModbusUtils.BytesForWords(count);
|
||||
Tools.AssertEqual(response[offset + 0], slave, "Slave mismatch got {0} expected {1}");
|
||||
Tools.AssertEqual(response[offset + 1], 4, "Function mismatch got {0} expected {1}");
|
||||
Tools.AssertEqual(response[offset + 2], bytes, "Bytes mismatch got {0} expected {1}");
|
||||
return ModbusUtils.DecodeWords(response, offset + 3, count);
|
||||
}
|
||||
|
||||
public object ApplyTo(IModbusModel model)
|
||||
{
|
||||
return model.getWIs(slave, address, count);
|
||||
}
|
||||
|
||||
public void FillResponse(byte[] response, int offset, object value)
|
||||
{
|
||||
var bytes = ModbusUtils.BytesForWords(count);
|
||||
response[offset + 0] = slave;
|
||||
response[offset + 1] = 4;
|
||||
response[offset + 2] = bytes;
|
||||
var data = ModbusUtils.EncodeWords(value as ushort[]);
|
||||
ModbusUtils.Copy(data, 0, response, offset + 3, bytes);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("[ModbusF04ReadInputRegisters Slave={0}, Address={1}, Count={2}]", slave, address, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
|
||||
namespace SharpModbus
|
||||
{
|
||||
public class ModbusF05WriteCoil : IModbusCommand
|
||||
{
|
||||
private readonly byte slave;
|
||||
private readonly ushort address;
|
||||
private readonly bool value;
|
||||
|
||||
public byte Code { get { return 5; } }
|
||||
public byte Slave { get { return slave; } }
|
||||
public ushort Address { get { return address; } }
|
||||
public bool Value { get { return value; } }
|
||||
public int RequestLength { get { return 6; } }
|
||||
public int ResponseLength { get { return 6; } }
|
||||
|
||||
public ModbusF05WriteCoil(byte slave, ushort address, bool state)
|
||||
{
|
||||
this.slave = slave;
|
||||
this.address = address;
|
||||
this.value = state;
|
||||
}
|
||||
|
||||
public void FillRequest(byte[] request, int offset)
|
||||
{
|
||||
request[offset + 0] = slave;
|
||||
request[offset + 1] = 5;
|
||||
request[offset + 2] = ModbusUtils.High(address);
|
||||
request[offset + 3] = ModbusUtils.Low(address);
|
||||
request[offset + 4] = ModbusUtils.EncodeBool(value);
|
||||
request[offset + 5] = 0;
|
||||
}
|
||||
|
||||
public object ParseResponse(byte[] response, int offset)
|
||||
{
|
||||
Tools.AssertEqual(response[offset + 0], slave, "Slave mismatch got {0} expected {1}");
|
||||
Tools.AssertEqual(response[offset + 1], 5, "Function mismatch got {0} expected {1}");
|
||||
Tools.AssertEqual(ModbusUtils.GetUShort(response, offset + 2), address, "Address mismatch got {0} expected {1}");
|
||||
Tools.AssertEqual(response[offset + 4], ModbusUtils.EncodeBool(value), "Value mismatch got {0} expected {1}");
|
||||
Tools.AssertEqual(response[offset + 5], 0, "Pad mismatch {0} expected:{1}");
|
||||
return null;
|
||||
}
|
||||
|
||||
public object ApplyTo(IModbusModel model)
|
||||
{
|
||||
model.setDO(slave, address, value);
|
||||
return null;
|
||||
}
|
||||
|
||||
public void FillResponse(byte[] response, int offset, object value)
|
||||
{
|
||||
FillRequest(response, offset);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("[ModbusF05WriteCoil Slave={0}, Address={1}, Value={2}]", slave, address, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
|
||||
namespace SharpModbus
|
||||
{
|
||||
public class ModbusF06WriteRegister : IModbusCommand
|
||||
{
|
||||
private readonly byte slave;
|
||||
private readonly ushort address;
|
||||
private readonly ushort value;
|
||||
|
||||
public byte Code { get { return 6; } }
|
||||
public byte Slave { get { return slave; } }
|
||||
public ushort Address { get { return address; } }
|
||||
public ushort Value { get { return value; } }
|
||||
public int RequestLength { get { return 6; } }
|
||||
public int ResponseLength { get { return 6; } }
|
||||
|
||||
public ModbusF06WriteRegister(byte slave, ushort address, ushort value)
|
||||
{
|
||||
this.slave = slave;
|
||||
this.address = address;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public void FillRequest(byte[] request, int offset)
|
||||
{
|
||||
request[offset + 0] = slave;
|
||||
request[offset + 1] = 6;
|
||||
request[offset + 2] = ModbusUtils.High(address);
|
||||
request[offset + 3] = ModbusUtils.Low(address);
|
||||
request[offset + 4] = ModbusUtils.High(value);
|
||||
request[offset + 5] = ModbusUtils.Low(value);
|
||||
}
|
||||
|
||||
public object ParseResponse(byte[] response, int offset)
|
||||
{
|
||||
Tools.AssertEqual(response[offset + 0], slave, "Slave mismatch got {0} expected {1}");
|
||||
Tools.AssertEqual(response[offset + 1], 6, "Function mismatch got {0} expected {1}");
|
||||
Tools.AssertEqual(ModbusUtils.GetUShort(response, offset + 2), address, "Address mismatch got {0} expected {1}");
|
||||
Tools.AssertEqual(ModbusUtils.GetUShort(response, offset + 4), value, "Value mismatch got {0} expected {1}");
|
||||
return null;
|
||||
}
|
||||
|
||||
public object ApplyTo(IModbusModel model)
|
||||
{
|
||||
model.setWO(slave, address, value);
|
||||
return null;
|
||||
}
|
||||
|
||||
public void FillResponse(byte[] response, int offset, object value)
|
||||
{
|
||||
FillRequest(response, offset);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("[ModbusF06WriteRegister Slave={0}, Address={1}, Value={2}]", slave, address, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
|
||||
namespace SharpModbus
|
||||
{
|
||||
public class ModbusF15WriteCoils : IModbusCommand
|
||||
{
|
||||
private readonly byte slave;
|
||||
private readonly ushort address;
|
||||
private readonly bool[] values;
|
||||
|
||||
public byte Code { get { return 15; } }
|
||||
public byte Slave { get { return slave; } }
|
||||
public ushort Address { get { return address; } }
|
||||
public bool[] Values { get { return ModbusUtils.Clone(values); } }
|
||||
public int RequestLength { get { return 7 + ModbusUtils.BytesForBools(values.Length); } }
|
||||
public int ResponseLength { get { return 6; } }
|
||||
|
||||
public ModbusF15WriteCoils(byte slave, ushort address, bool[] values)
|
||||
{
|
||||
this.slave = slave;
|
||||
this.address = address;
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
public void FillRequest(byte[] request, int offset)
|
||||
{
|
||||
FillResponse(request, offset, null);
|
||||
var bytes = ModbusUtils.EncodeBools(values);
|
||||
request[offset + 6] = (byte)bytes.Length;
|
||||
ModbusUtils.Copy(bytes, 0, request, offset + 7, bytes.Length);
|
||||
}
|
||||
|
||||
public object ParseResponse(byte[] response, int offset)
|
||||
{
|
||||
Tools.AssertEqual(response[offset + 0], slave, "Slave mismatch got {0} expected {1}");
|
||||
Tools.AssertEqual(response[offset + 1], 15, "Function mismatch got {0} expected {1}");
|
||||
Tools.AssertEqual(ModbusUtils.GetUShort(response, offset + 2), address, "Address mismatch got {0} expected {1}");
|
||||
Tools.AssertEqual(ModbusUtils.GetUShort(response, offset + 4), values.Length, "Coil count mismatch got {0} expected {1}");
|
||||
return null;
|
||||
}
|
||||
|
||||
public object ApplyTo(IModbusModel model)
|
||||
{
|
||||
model.setDOs(slave, address, values);
|
||||
return null;
|
||||
}
|
||||
|
||||
public void FillResponse(byte[] response, int offset, object value)
|
||||
{
|
||||
response[offset + 0] = slave;
|
||||
response[offset + 1] = 15;
|
||||
response[offset + 2] = ModbusUtils.High(address);
|
||||
response[offset + 3] = ModbusUtils.Low(address);
|
||||
response[offset + 4] = ModbusUtils.High(values.Length);
|
||||
response[offset + 5] = ModbusUtils.Low(values.Length);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("[ModbusF15WriteCoils Slave={0}, Address={1}, Values={2}]", slave, address, values);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
|
||||
namespace SharpModbus
|
||||
{
|
||||
public class ModbusF16WriteRegisters : IModbusCommand
|
||||
{
|
||||
private readonly byte slave;
|
||||
private readonly ushort address;
|
||||
private readonly ushort[] values;
|
||||
|
||||
public byte Code { get { return 16; } }
|
||||
public byte Slave { get { return slave; } }
|
||||
public ushort Address { get { return address; } }
|
||||
public ushort[] Values { get { return ModbusUtils.Clone(values); } }
|
||||
public int RequestLength { get { return 7 + ModbusUtils.BytesForWords(values.Length); } }
|
||||
public int ResponseLength { get { return 6; } }
|
||||
|
||||
public ModbusF16WriteRegisters(byte slave, ushort address, ushort[] values)
|
||||
{
|
||||
this.slave = slave;
|
||||
this.address = address;
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
public void FillRequest(byte[] request, int offset)
|
||||
{
|
||||
FillResponse(request, offset, null);
|
||||
var bytes = ModbusUtils.EncodeWords(values);
|
||||
request[offset + 6] = (byte)bytes.Length;
|
||||
ModbusUtils.Copy(bytes, 0, request, offset + 7, bytes.Length);
|
||||
}
|
||||
|
||||
public object ParseResponse(byte[] response, int offset)
|
||||
{
|
||||
Tools.AssertEqual(response[offset + 0], slave, "Slave mismatch got {0} expected {1}");
|
||||
Tools.AssertEqual(response[offset + 1], 16, "Function mismatch got {0} expected {1}");
|
||||
Tools.AssertEqual(ModbusUtils.GetUShort(response, offset + 2), address, "Address mismatch got {0} expected {1}");
|
||||
Tools.AssertEqual(ModbusUtils.GetUShort(response, offset + 4), values.Length, "Register count mismatch got {0} expected {1}");
|
||||
return null;
|
||||
}
|
||||
|
||||
public object ApplyTo(IModbusModel model)
|
||||
{
|
||||
model.setWOs(slave, address, values);
|
||||
return null;
|
||||
}
|
||||
|
||||
public void FillResponse(byte[] response, int offset, object value)
|
||||
{
|
||||
response[offset + 0] = slave;
|
||||
response[offset + 1] = 16;
|
||||
response[offset + 2] = ModbusUtils.High(address);
|
||||
response[offset + 3] = ModbusUtils.Low(address);
|
||||
response[offset + 4] = ModbusUtils.High(values.Length);
|
||||
response[offset + 5] = ModbusUtils.Low(values.Length);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("[ModbusF16WriteRegisters Slave={0}, Address={1}, Values={2}]", slave, address, values);
|
||||
}
|
||||
}
|
||||
}
|
||||
18
常用工具集/Utility/Network/Modbus/SharpModbus/IModbusCommand.cs
Normal file
18
常用工具集/Utility/Network/Modbus/SharpModbus/IModbusCommand.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
using System;
|
||||
|
||||
namespace SharpModbus
|
||||
{
|
||||
public interface IModbusCommand
|
||||
{
|
||||
byte Code { get; }
|
||||
byte Slave { get; }
|
||||
ushort Address { get; }
|
||||
int RequestLength { get; }
|
||||
int ResponseLength { get; }
|
||||
void FillRequest(byte[] request, int offset);
|
||||
object ParseResponse(byte[] response, int offset);
|
||||
object ApplyTo(IModbusModel model);
|
||||
void FillResponse(byte[] response, int offset, object value);
|
||||
}
|
||||
}
|
||||
39
常用工具集/Utility/Network/Modbus/SharpModbus/IModbusModel.cs
Normal file
39
常用工具集/Utility/Network/Modbus/SharpModbus/IModbusModel.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
|
||||
namespace SharpModbus
|
||||
{
|
||||
public interface IModbusModel
|
||||
{
|
||||
void setDI(byte slave, ushort address, bool value);
|
||||
|
||||
void setDIs(byte slave, ushort address, bool[] values);
|
||||
|
||||
bool getDI(byte slave, ushort address);
|
||||
|
||||
bool[] getDIs(byte slave, ushort address, int count);
|
||||
|
||||
void setDO(byte slave, ushort address, bool value);
|
||||
|
||||
void setDOs(byte slave, ushort address, bool[] values);
|
||||
|
||||
bool getDO(byte slave, ushort address);
|
||||
|
||||
bool[] getDOs(byte slave, ushort address, int count);
|
||||
|
||||
void setWI(byte slave, ushort address, ushort value);
|
||||
|
||||
void setWIs(byte slave, ushort address, ushort[] values);
|
||||
|
||||
ushort getWI(byte slave, ushort address);
|
||||
|
||||
ushort[] getWIs(byte slave, ushort address, int count);
|
||||
|
||||
void setWO(byte slave, ushort address, ushort value);
|
||||
|
||||
void setWOs(byte slave, ushort address, ushort[] values);
|
||||
|
||||
ushort getWO(byte slave, ushort address);
|
||||
|
||||
ushort[] getWOs(byte slave, ushort address, int count);
|
||||
}
|
||||
}
|
||||
10
常用工具集/Utility/Network/Modbus/SharpModbus/IModbusProtocol.cs
Normal file
10
常用工具集/Utility/Network/Modbus/SharpModbus/IModbusProtocol.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace SharpModbus
|
||||
{
|
||||
public interface IModbusProtocol
|
||||
{
|
||||
IModbusWrapper Wrap(IModbusCommand wrapped);
|
||||
IModbusWrapper Parse(byte[] request, int offset);
|
||||
}
|
||||
}
|
||||
10
常用工具集/Utility/Network/Modbus/SharpModbus/IModbusScanner.cs
Normal file
10
常用工具集/Utility/Network/Modbus/SharpModbus/IModbusScanner.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace SharpModbus
|
||||
{
|
||||
public interface IModbusScanner
|
||||
{
|
||||
void Append(byte[] data, int offset, int count);
|
||||
IModbusWrapper Scan();
|
||||
}
|
||||
}
|
||||
10
常用工具集/Utility/Network/Modbus/SharpModbus/IModbusStream.cs
Normal file
10
常用工具集/Utility/Network/Modbus/SharpModbus/IModbusStream.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace SharpModbus
|
||||
{
|
||||
public interface IModbusStream : IDisposable
|
||||
{
|
||||
void Write(byte[] data);
|
||||
int Read(byte[] data);
|
||||
}
|
||||
}
|
||||
11
常用工具集/Utility/Network/Modbus/SharpModbus/IModbusWrapper.cs
Normal file
11
常用工具集/Utility/Network/Modbus/SharpModbus/IModbusWrapper.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace SharpModbus
|
||||
{
|
||||
public interface IModbusWrapper : IModbusCommand
|
||||
{
|
||||
IModbusCommand Wrapped { get; }
|
||||
byte[] GetException(byte code);
|
||||
void CheckException(byte[] respose, int count);
|
||||
}
|
||||
}
|
||||
17
常用工具集/Utility/Network/Modbus/SharpModbus/ModbusException.cs
Normal file
17
常用工具集/Utility/Network/Modbus/SharpModbus/ModbusException.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
|
||||
namespace SharpModbus
|
||||
{
|
||||
public class ModbusException : Exception
|
||||
{
|
||||
private readonly byte code;
|
||||
|
||||
public byte Code { get { return code; } }
|
||||
|
||||
public ModbusException(byte code) :
|
||||
base(string.Format("Modbus exception {0}", code))
|
||||
{
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using SharpSerial;
|
||||
|
||||
namespace SharpModbus
|
||||
{
|
||||
public class ModbusIsolatedStream : IModbusStream
|
||||
{
|
||||
private readonly Action<char, byte[], int> monitor;
|
||||
private readonly SerialProcess serialProcess;
|
||||
private readonly int timeout;
|
||||
|
||||
public ModbusIsolatedStream(object settings, int timeout, Action<char, byte[], int> monitor = null)
|
||||
{
|
||||
this.serialProcess = new SerialProcess(settings);
|
||||
this.timeout = timeout;
|
||||
this.monitor = monitor;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Tools.Dispose(serialProcess);
|
||||
}
|
||||
|
||||
public void Write(byte[] data)
|
||||
{
|
||||
if (monitor != null) monitor('>', data, data.Length);
|
||||
serialProcess.Write(data);
|
||||
}
|
||||
|
||||
public int Read(byte[] data)
|
||||
{
|
||||
var response = serialProcess.Read(data.Length, -1, timeout);
|
||||
var count = response.Length;
|
||||
for (var i = 0; i < count; i++) data[i] = response[i];
|
||||
if (monitor != null) monitor('<', data, count);
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
||||
132
常用工具集/Utility/Network/Modbus/SharpModbus/ModbusMaster.cs
Normal file
132
常用工具集/Utility/Network/Modbus/SharpModbus/ModbusMaster.cs
Normal file
@@ -0,0 +1,132 @@
|
||||
using MES.Utility.Core;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace SharpModbus
|
||||
{
|
||||
public class ModbusMaster : IDisposable
|
||||
{
|
||||
public delegate void OnSendedData(byte[] bytes, string hexString);
|
||||
public event OnSendedData OnSended;
|
||||
|
||||
public delegate void OnRecivedData(byte[] bytes, string hexString);
|
||||
public event OnRecivedData OnRecived;
|
||||
|
||||
public static ModbusMaster RTU(SerialSettings settings, int timeout = 400)
|
||||
{
|
||||
var stream = new ModbusSerialStream(settings, timeout);
|
||||
var protocol = new ModbusRTUProtocol();
|
||||
return new ModbusMaster(stream, protocol);
|
||||
}
|
||||
|
||||
public static ModbusMaster IsolatedRTU(SerialSettings settings, int timeout = 400)
|
||||
{
|
||||
var stream = new ModbusIsolatedStream(settings, timeout);
|
||||
var protocol = new ModbusRTUProtocol();
|
||||
return new ModbusMaster(stream, protocol);
|
||||
}
|
||||
|
||||
public static ModbusMaster TCP(string ip, int port, int timeout = 400)
|
||||
{
|
||||
var socket = Tools.ConnectWithTimeout(ip, port, timeout);
|
||||
var stream = new ModbusSocketStream(socket, timeout);
|
||||
var protocol = new ModbusTCPProtocol();
|
||||
return new ModbusMaster(stream, protocol);
|
||||
}
|
||||
|
||||
private readonly IModbusProtocol protocol;
|
||||
private readonly IModbusStream stream;
|
||||
|
||||
public ModbusMaster(IModbusStream stream, IModbusProtocol protocol)
|
||||
{
|
||||
this.stream = stream;
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Tools.Dispose(stream);
|
||||
}
|
||||
|
||||
public bool ReadCoil(byte slave, ushort address)
|
||||
{
|
||||
return ReadCoils(slave, address, 1)[0]; //there is no code for single read
|
||||
}
|
||||
|
||||
public bool ReadInput(byte slave, ushort address)
|
||||
{
|
||||
return ReadInputs(slave, address, 1)[0]; //there is no code for single read
|
||||
}
|
||||
|
||||
public ushort ReadInputRegister(byte slave, ushort address)
|
||||
{
|
||||
return ReadInputRegisters(slave, address, 1)[0]; //there is no code for single read
|
||||
}
|
||||
|
||||
public ushort ReadHoldingRegister(byte slave, ushort address)
|
||||
{
|
||||
return ReadHoldingRegisters(slave, address, 1)[0]; //there is no code for single read
|
||||
}
|
||||
|
||||
public bool[] ReadCoils(byte slave, ushort address, ushort count)
|
||||
{
|
||||
return Execute(new ModbusF01ReadCoils(slave, address, count)) as bool[];
|
||||
}
|
||||
|
||||
public bool[] ReadInputs(byte slave, ushort address, ushort count)
|
||||
{
|
||||
return Execute(new ModbusF02ReadInputs(slave, address, count)) as bool[];
|
||||
}
|
||||
|
||||
public ushort[] ReadInputRegisters(byte slave, ushort address, ushort count)
|
||||
{
|
||||
return Execute(new ModbusF04ReadInputRegisters(slave, address, count)) as ushort[];
|
||||
}
|
||||
|
||||
public ushort[] ReadHoldingRegisters(byte slave, ushort address, ushort count)
|
||||
{
|
||||
return Execute(new ModbusF03ReadHoldingRegisters(slave, address, count)) as ushort[];
|
||||
}
|
||||
|
||||
public void WriteCoil(byte slave, ushort address, bool value)
|
||||
{
|
||||
Execute(new ModbusF05WriteCoil(slave, address, value));
|
||||
}
|
||||
|
||||
public void WriteRegister(byte slave, ushort address, ushort value)
|
||||
{
|
||||
Execute(new ModbusF06WriteRegister(slave, address, value));
|
||||
}
|
||||
|
||||
public void WriteCoils(byte slave, ushort address, params bool[] values)
|
||||
{
|
||||
Execute(new ModbusF15WriteCoils(slave, address, values));
|
||||
}
|
||||
|
||||
public void WriteRegisters(byte slave, ushort address, params ushort[] values)
|
||||
{
|
||||
Execute(new ModbusF16WriteRegisters(slave, address, values));
|
||||
}
|
||||
|
||||
private object Execute(IModbusCommand cmd)
|
||||
{
|
||||
var wrapper = protocol.Wrap(cmd);
|
||||
var request = new byte[wrapper.RequestLength];
|
||||
var response = new byte[wrapper.ResponseLength];
|
||||
wrapper.FillRequest(request, 0);
|
||||
OnSended?.Invoke(request, DataHexString(request));
|
||||
stream.Write(request);
|
||||
var count = stream.Read(response);
|
||||
OnRecived?.Invoke(response, DataHexString(response));
|
||||
if (count < response.Length) wrapper.CheckException(response, count);
|
||||
return wrapper.ParseResponse(response, 0);
|
||||
}
|
||||
|
||||
private string DataHexString(byte[] bytes)
|
||||
{
|
||||
if (bytes == null)
|
||||
return string.Empty;
|
||||
return bytes.Select(it => Convert.ToString(it, 16).PadLeft(2, '0').ToUpper()).ToList().GetStrArray(" ");
|
||||
}
|
||||
}
|
||||
}
|
||||
152
常用工具集/Utility/Network/Modbus/SharpModbus/ModbusModel.cs
Normal file
152
常用工具集/Utility/Network/Modbus/SharpModbus/ModbusModel.cs
Normal file
@@ -0,0 +1,152 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SharpModbus
|
||||
{
|
||||
public enum ModbusIoType
|
||||
{
|
||||
DI,
|
||||
DO,
|
||||
WO,
|
||||
WI
|
||||
}
|
||||
|
||||
public class ModbusModel : IModbusModel
|
||||
{
|
||||
private readonly IDictionary<string, bool> digitals = new Dictionary<string, bool>();
|
||||
private readonly IDictionary<string, ushort> words = new Dictionary<string, ushort>();
|
||||
|
||||
public void setDI(byte slave, ushort address, bool value)
|
||||
{
|
||||
var key = Key(ModbusIoType.DI, slave, address);
|
||||
digitals[key] = value;
|
||||
}
|
||||
|
||||
public void setDIs(byte slave, ushort address, bool[] values)
|
||||
{
|
||||
for (var i = 0; i < values.Length; i++)
|
||||
{
|
||||
var key = Key(ModbusIoType.DI, slave, address + i);
|
||||
digitals[key] = values[i];
|
||||
}
|
||||
}
|
||||
|
||||
public bool getDI(byte slave, ushort address)
|
||||
{
|
||||
var key = Key(ModbusIoType.DI, slave, address);
|
||||
return digitals[key];
|
||||
}
|
||||
|
||||
public bool[] getDIs(byte slave, ushort address, int count)
|
||||
{
|
||||
var values = new bool[count];
|
||||
for (var i = 0; i < values.Length; i++)
|
||||
{
|
||||
var key = Key(ModbusIoType.DI, slave, address + i);
|
||||
values[i] = digitals[key];
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
public void setDO(byte slave, ushort address, bool value)
|
||||
{
|
||||
var key = Key(ModbusIoType.DO, slave, address);
|
||||
digitals[key] = value;
|
||||
}
|
||||
|
||||
public void setDOs(byte slave, ushort address, bool[] values)
|
||||
{
|
||||
for (var i = 0; i < values.Length; i++)
|
||||
{
|
||||
var key = Key(ModbusIoType.DO, slave, address + i);
|
||||
digitals[key] = values[i];
|
||||
}
|
||||
}
|
||||
|
||||
public bool getDO(byte slave, ushort address)
|
||||
{
|
||||
var key = Key(ModbusIoType.DO, slave, address);
|
||||
return digitals[key];
|
||||
}
|
||||
|
||||
public bool[] getDOs(byte slave, ushort address, int count)
|
||||
{
|
||||
var values = new bool[count];
|
||||
for (var i = 0; i < values.Length; i++)
|
||||
{
|
||||
var key = Key(ModbusIoType.DO, slave, address + i);
|
||||
values[i] = digitals[key];
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
public void setWI(byte slave, ushort address, ushort value)
|
||||
{
|
||||
var key = Key(ModbusIoType.WI, slave, address);
|
||||
words[key] = value;
|
||||
}
|
||||
|
||||
public void setWIs(byte slave, ushort address, ushort[] values)
|
||||
{
|
||||
for (var i = 0; i < values.Length; i++)
|
||||
{
|
||||
var key = Key(ModbusIoType.WI, slave, address + i);
|
||||
words[key] = values[i];
|
||||
}
|
||||
}
|
||||
|
||||
public ushort getWI(byte slave, ushort address)
|
||||
{
|
||||
var key = Key(ModbusIoType.WI, slave, address);
|
||||
return words[key];
|
||||
}
|
||||
|
||||
public ushort[] getWIs(byte slave, ushort address, int count)
|
||||
{
|
||||
var values = new ushort[count];
|
||||
for (var i = 0; i < values.Length; i++)
|
||||
{
|
||||
var key = Key(ModbusIoType.WI, slave, address + i);
|
||||
values[i] = words[key];
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
public void setWO(byte slave, ushort address, ushort value)
|
||||
{
|
||||
var key = Key(ModbusIoType.WO, slave, address);
|
||||
words[key] = value;
|
||||
}
|
||||
|
||||
public void setWOs(byte slave, ushort address, ushort[] values)
|
||||
{
|
||||
for (var i = 0; i < values.Length; i++)
|
||||
{
|
||||
var key = Key(ModbusIoType.WO, slave, address + i);
|
||||
words[key] = values[i];
|
||||
}
|
||||
}
|
||||
|
||||
public ushort getWO(byte slave, ushort address)
|
||||
{
|
||||
var key = Key(ModbusIoType.WO, slave, address);
|
||||
return words[key];
|
||||
}
|
||||
|
||||
public ushort[] getWOs(byte slave, ushort address, int count)
|
||||
{
|
||||
var values = new ushort[count];
|
||||
for (var i = 0; i < values.Length; i++)
|
||||
{
|
||||
var key = Key(ModbusIoType.WO, slave, address + i);
|
||||
values[i] = words[key];
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
private string Key(ModbusIoType type, byte slave, int address)
|
||||
{
|
||||
return string.Format("{0},{1},{2}", slave, type, address);
|
||||
}
|
||||
}
|
||||
}
|
||||
90
常用工具集/Utility/Network/Modbus/SharpModbus/ModbusParser.cs
Normal file
90
常用工具集/Utility/Network/Modbus/SharpModbus/ModbusParser.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
using System;
|
||||
|
||||
namespace SharpModbus
|
||||
{
|
||||
public static class ModbusParser
|
||||
{
|
||||
public static IModbusCommand Parse(byte[] request, int offset)
|
||||
{
|
||||
var slave = request[offset + 0];
|
||||
var code = request[offset + 1];
|
||||
var address = ModbusUtils.GetUShort(request, offset + 2);
|
||||
switch (code)
|
||||
{
|
||||
case 1:
|
||||
return Parse01(slave, code, address, request, offset);
|
||||
case 2:
|
||||
return Parse02(slave, code, address, request, offset);
|
||||
case 3:
|
||||
return Parse03(slave, code, address, request, offset);
|
||||
case 4:
|
||||
return Parse04(slave, code, address, request, offset);
|
||||
case 5:
|
||||
return Parse05(slave, code, address, request, offset);
|
||||
case 6:
|
||||
return Parse06(slave, code, address, request, offset);
|
||||
case 15:
|
||||
return Parse15(slave, code, address, request, offset);
|
||||
case 16:
|
||||
return Parse16(slave, code, address, request, offset);
|
||||
}
|
||||
throw Tools.Make("Unsupported function code {0}", code);
|
||||
}
|
||||
|
||||
private static IModbusCommand Parse01(byte slave, byte code, ushort address, byte[] request, int offset)
|
||||
{
|
||||
var count = ModbusUtils.GetUShort(request, offset + 4);
|
||||
return new ModbusF01ReadCoils(slave, address, count);
|
||||
}
|
||||
|
||||
private static IModbusCommand Parse02(byte slave, byte code, ushort address, byte[] request, int offset)
|
||||
{
|
||||
var count = ModbusUtils.GetUShort(request, offset + 4);
|
||||
return new ModbusF02ReadInputs(slave, address, count);
|
||||
}
|
||||
|
||||
private static IModbusCommand Parse03(byte slave, byte code, ushort address, byte[] request, int offset)
|
||||
{
|
||||
var count = ModbusUtils.GetUShort(request, offset + 4);
|
||||
return new ModbusF03ReadHoldingRegisters(slave, address, count);
|
||||
}
|
||||
|
||||
private static IModbusCommand Parse04(byte slave, byte code, ushort address, byte[] request, int offset)
|
||||
{
|
||||
var count = ModbusUtils.GetUShort(request, offset + 4);
|
||||
return new ModbusF04ReadInputRegisters(slave, address, count);
|
||||
}
|
||||
|
||||
private static IModbusCommand Parse05(byte slave, byte code, ushort address, byte[] request, int offset)
|
||||
{
|
||||
var value = ModbusUtils.DecodeBool(request[offset + 4]);
|
||||
var zero = request[offset + 5];
|
||||
Tools.AssertEqual(zero, 0, "Zero mismatch got {0} expected {1}");
|
||||
return new ModbusF05WriteCoil(slave, address, value);
|
||||
}
|
||||
|
||||
private static IModbusCommand Parse06(byte slave, byte code, ushort address, byte[] request, int offset)
|
||||
{
|
||||
var value = ModbusUtils.GetUShort(request, offset + 4);
|
||||
return new ModbusF06WriteRegister(slave, address, value);
|
||||
}
|
||||
|
||||
private static IModbusCommand Parse15(byte slave, byte code, ushort address, byte[] request, int offset)
|
||||
{
|
||||
var count = ModbusUtils.GetUShort(request, offset + 4);
|
||||
var values = ModbusUtils.DecodeBools(request, offset + 7, count);
|
||||
var bytes = request[offset + 6];
|
||||
Tools.AssertEqual(ModbusUtils.BytesForBools(count), bytes, "Byte count mismatch got {0} expected {1}");
|
||||
return new ModbusF15WriteCoils(slave, address, values);
|
||||
}
|
||||
|
||||
private static IModbusCommand Parse16(byte slave, byte code, ushort address, byte[] request, int offset)
|
||||
{
|
||||
var count = ModbusUtils.GetUShort(request, offset + 4);
|
||||
var values = ModbusUtils.DecodeWords(request, offset + 7, count);
|
||||
var bytes = request[offset + 6];
|
||||
Tools.AssertEqual(ModbusUtils.BytesForWords(count), bytes, "Byte count mismatch got {0} expected {1}");
|
||||
return new ModbusF16WriteRegisters(slave, address, values);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
|
||||
namespace SharpModbus
|
||||
{
|
||||
public class ModbusRTUProtocol : IModbusProtocol
|
||||
{
|
||||
public IModbusWrapper Wrap(IModbusCommand wrapped)
|
||||
{
|
||||
return new ModbusRTUWrapper(wrapped);
|
||||
}
|
||||
|
||||
public IModbusWrapper Parse(byte[] request, int offset)
|
||||
{
|
||||
var wrapped = ModbusParser.Parse(request, offset);
|
||||
var crc = ModbusUtils.CRC16(request, offset, wrapped.RequestLength);
|
||||
Tools.AssertEqual(crc, ModbusUtils.GetUShortLittleEndian(request, offset + wrapped.RequestLength),
|
||||
"CRC mismatch {0:X4} expected {1:X4}");
|
||||
return new ModbusRTUWrapper(wrapped);
|
||||
}
|
||||
}
|
||||
}
|
||||
47
常用工具集/Utility/Network/Modbus/SharpModbus/ModbusRTUScanner.cs
Normal file
47
常用工具集/Utility/Network/Modbus/SharpModbus/ModbusRTUScanner.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SharpModbus
|
||||
{
|
||||
public class ModbusRTUScanner : IModbusScanner
|
||||
{
|
||||
private readonly ModbusRTUProtocol protocol = new ModbusRTUProtocol();
|
||||
private readonly List<byte> buffer = new List<byte>();
|
||||
|
||||
public void Append(byte[] data, int offset, int count)
|
||||
{
|
||||
for (var i = 0; i < count; i++) buffer.Add(data[offset + i]);
|
||||
}
|
||||
|
||||
public IModbusWrapper Scan()
|
||||
{
|
||||
//01,02,03,04,05,06 have 6 + 2(CRC)
|
||||
//15,16 have 6 + 1(len) + len + 2(CRC)
|
||||
if (buffer.Count >= 8)
|
||||
{
|
||||
var code = buffer[1];
|
||||
CheckCode(code);
|
||||
var length = 8;
|
||||
if (HasBytesAt6(code)) length += 1 + buffer[6];
|
||||
if (buffer.Count >= length)
|
||||
{
|
||||
var request = buffer.GetRange(0, length).ToArray();
|
||||
buffer.RemoveRange(0, length);
|
||||
return protocol.Parse(request, 0);
|
||||
}
|
||||
}
|
||||
return null; //not enough data to parse
|
||||
}
|
||||
|
||||
bool HasBytesAt6(byte code)
|
||||
{
|
||||
return "15,16".Contains(code.ToString("00"));
|
||||
}
|
||||
|
||||
void CheckCode(byte code)
|
||||
{
|
||||
var valid = "01,02,03,04,05,06,15,16".Contains(code.ToString("00"));
|
||||
if (!valid) Tools.Throw("Unsupported code {0}", code);
|
||||
}
|
||||
}
|
||||
}
|
||||
85
常用工具集/Utility/Network/Modbus/SharpModbus/ModbusRTUWrapper.cs
Normal file
85
常用工具集/Utility/Network/Modbus/SharpModbus/ModbusRTUWrapper.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
using System;
|
||||
|
||||
namespace SharpModbus
|
||||
{
|
||||
public class ModbusRTUWrapper : IModbusWrapper
|
||||
{
|
||||
private readonly IModbusCommand wrapped;
|
||||
|
||||
public byte Code { get { return wrapped.Code; } }
|
||||
public byte Slave { get { return wrapped.Slave; } }
|
||||
public ushort Address { get { return wrapped.Address; } }
|
||||
public IModbusCommand Wrapped { get { return wrapped; } }
|
||||
public int RequestLength { get { return wrapped.RequestLength + 2; } }
|
||||
public int ResponseLength { get { return wrapped.ResponseLength + 2; } }
|
||||
|
||||
public ModbusRTUWrapper(IModbusCommand wrapped)
|
||||
{
|
||||
this.wrapped = wrapped;
|
||||
}
|
||||
|
||||
public void FillRequest(byte[] request, int offset)
|
||||
{
|
||||
wrapped.FillRequest(request, offset);
|
||||
var crc = ModbusUtils.CRC16(request, offset, wrapped.RequestLength);
|
||||
request[offset + wrapped.RequestLength + 0] = ModbusUtils.Low(crc);
|
||||
request[offset + wrapped.RequestLength + 1] = ModbusUtils.High(crc);
|
||||
}
|
||||
|
||||
public object ParseResponse(byte[] response, int offset)
|
||||
{
|
||||
var crc1 = ModbusUtils.CRC16(response, offset, wrapped.ResponseLength);
|
||||
//crc is little endian page 13 http://modbus.org/docs/Modbus_over_serial_line_V1_02.pdf
|
||||
var crc2 = ModbusUtils.GetUShortLittleEndian(response, offset + wrapped.ResponseLength);
|
||||
Tools.AssertEqual(crc2, crc1, "CRC mismatch got {0:X4} expected {1:X4}");
|
||||
return wrapped.ParseResponse(response, offset);
|
||||
}
|
||||
|
||||
public object ApplyTo(IModbusModel model)
|
||||
{
|
||||
return wrapped.ApplyTo(model);
|
||||
}
|
||||
|
||||
public void FillResponse(byte[] response, int offset, object value)
|
||||
{
|
||||
wrapped.FillResponse(response, offset, value);
|
||||
var crc = ModbusUtils.CRC16(response, offset, wrapped.ResponseLength);
|
||||
response[offset + wrapped.ResponseLength + 0] = ModbusUtils.Low(crc);
|
||||
response[offset + wrapped.ResponseLength + 1] = ModbusUtils.High(crc);
|
||||
}
|
||||
|
||||
public byte[] GetException(byte code)
|
||||
{
|
||||
var exception = new byte[5];
|
||||
exception[0] = wrapped.Slave;
|
||||
exception[1] = (byte)(wrapped.Code | 0x80);
|
||||
exception[2] = code;
|
||||
var crc = ModbusUtils.CRC16(exception, 0, 3);
|
||||
exception[3] = ModbusUtils.Low(crc);
|
||||
exception[4] = ModbusUtils.High(crc);
|
||||
return exception;
|
||||
}
|
||||
|
||||
public void CheckException(byte[] response, int count)
|
||||
{
|
||||
if (count < 5) Tools.Throw("Partial packet exception got {0} expected >= {1}", count, 5);
|
||||
var offset = 0;
|
||||
var code = response[offset + 1];
|
||||
if ((code & 0x80) != 0)
|
||||
{
|
||||
Tools.AssertEqual(response[offset + 0], wrapped.Slave, "Slave mismatch got {0} expected {1}");
|
||||
Tools.AssertEqual(code & 0x7F, wrapped.Code, "Code mismatch got {0} expected {1}");
|
||||
var crc1 = ModbusUtils.CRC16(response, offset, 3);
|
||||
//crc is little endian page 13 http://modbus.org/docs/Modbus_over_serial_line_V1_02.pdf
|
||||
var crc2 = ModbusUtils.GetUShortLittleEndian(response, offset + 3);
|
||||
Tools.AssertEqual(crc2, crc1, "CRC mismatch got {0:X4} expected {1:X4}");
|
||||
throw new ModbusException(response[offset + 2]);
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("[ModbusRTUWrapper Wrapped={0}]", wrapped);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using SharpSerial;
|
||||
|
||||
namespace SharpModbus
|
||||
{
|
||||
public class ModbusSerialStream : IModbusStream
|
||||
{
|
||||
private readonly Action<char, byte[], int> monitor;
|
||||
private readonly SerialDevice serialDevice;
|
||||
private readonly int timeout;
|
||||
|
||||
public ModbusSerialStream(SerialSettings settings, int timeout, Action<char, byte[], int> monitor = null)
|
||||
{
|
||||
this.serialDevice = new SerialDevice(settings);
|
||||
this.timeout = timeout;
|
||||
this.monitor = monitor;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Tools.Dispose(serialDevice);
|
||||
}
|
||||
|
||||
public void Write(byte[] data)
|
||||
{
|
||||
if (monitor != null) monitor('>', data, data.Length);
|
||||
serialDevice.Write(data);
|
||||
}
|
||||
|
||||
public int Read(byte[] data)
|
||||
{
|
||||
var response = serialDevice.Read(data.Length, -1, timeout);
|
||||
var count = response.Length;
|
||||
for (var i = 0; i < count; i++) data[i] = response[i];
|
||||
if (monitor != null) monitor('<', data, count);
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace SharpModbus
|
||||
{
|
||||
public class ModbusSocketStream : IModbusStream
|
||||
{
|
||||
private readonly TcpClient socket;
|
||||
private readonly Action<char, byte[], int> monitor;
|
||||
|
||||
public ModbusSocketStream(TcpClient socket, int timeout, Action<char, byte[], int> monitor = null)
|
||||
{
|
||||
socket.ReceiveTimeout = timeout;
|
||||
socket.SendTimeout = timeout;
|
||||
this.monitor = monitor;
|
||||
this.socket = socket;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Tools.Dispose(socket);
|
||||
}
|
||||
|
||||
public void Write(byte[] data)
|
||||
{
|
||||
//implicit discard to allow retry after timeout as per #5
|
||||
while (socket.Available > 0) socket.GetStream().ReadByte();
|
||||
if (monitor != null) monitor('>', data, data.Length);
|
||||
socket.GetStream().Write(data, 0, data.Length);
|
||||
}
|
||||
|
||||
public int Read(byte[] data)
|
||||
{
|
||||
var count = 0;
|
||||
var dl = DateTime.Now.AddMilliseconds(socket.ReceiveTimeout);
|
||||
while (count < data.Length)
|
||||
{
|
||||
var available = socket.Available;
|
||||
if (available == 0)
|
||||
{
|
||||
if (DateTime.Now > dl) break;
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
var size = (int)Math.Min(available, data.Length - count);
|
||||
count += socket.GetStream().Read(data, count, size);
|
||||
dl = DateTime.Now; //should come in single packet
|
||||
}
|
||||
}
|
||||
if (monitor != null) monitor('<', data, count);
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
|
||||
namespace SharpModbus
|
||||
{
|
||||
public class ModbusTCPProtocol : IModbusProtocol
|
||||
{
|
||||
private ushort transactionId = 0;
|
||||
|
||||
public ushort TransactionId
|
||||
{
|
||||
get { return transactionId; }
|
||||
set { transactionId = value; }
|
||||
}
|
||||
|
||||
public IModbusWrapper Wrap(IModbusCommand wrapped)
|
||||
{
|
||||
return new ModbusTCPWrapper(wrapped, transactionId++);
|
||||
}
|
||||
|
||||
public IModbusWrapper Parse(byte[] request, int offset)
|
||||
{
|
||||
var wrapped = ModbusParser.Parse(request, offset + 6);
|
||||
Tools.AssertEqual(wrapped.RequestLength, ModbusUtils.GetUShort(request, offset + 4),
|
||||
"RequestLength mismatch got {0} expected {1}");
|
||||
var transaction = ModbusUtils.GetUShort(request, offset);
|
||||
return new ModbusTCPWrapper(wrapped, transaction);
|
||||
}
|
||||
}
|
||||
}
|
||||
31
常用工具集/Utility/Network/Modbus/SharpModbus/ModbusTCPScanner.cs
Normal file
31
常用工具集/Utility/Network/Modbus/SharpModbus/ModbusTCPScanner.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SharpModbus
|
||||
{
|
||||
public class ModbusTCPScanner : IModbusScanner
|
||||
{
|
||||
private readonly ModbusTCPProtocol protocol = new ModbusTCPProtocol();
|
||||
private readonly List<byte> buffer = new List<byte>();
|
||||
|
||||
public void Append(byte[] data, int offset, int count)
|
||||
{
|
||||
for (var i = 0; i < count; i++) buffer.Add(data[offset + i]);
|
||||
}
|
||||
|
||||
public IModbusWrapper Scan()
|
||||
{
|
||||
if (buffer.Count >= 6)
|
||||
{
|
||||
var length = ModbusUtils.GetUShort(buffer[4], buffer[5]);
|
||||
if (buffer.Count >= 6 + length)
|
||||
{
|
||||
var request = buffer.GetRange(0, 6 + length).ToArray();
|
||||
buffer.RemoveRange(0, 6 + length);
|
||||
return protocol.Parse(request, 0);
|
||||
}
|
||||
}
|
||||
return null; //not enough data to parse
|
||||
}
|
||||
}
|
||||
}
|
||||
92
常用工具集/Utility/Network/Modbus/SharpModbus/ModbusTCPWrapper.cs
Normal file
92
常用工具集/Utility/Network/Modbus/SharpModbus/ModbusTCPWrapper.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
|
||||
namespace SharpModbus
|
||||
{
|
||||
public class ModbusTCPWrapper : IModbusWrapper
|
||||
{
|
||||
private readonly IModbusCommand wrapped;
|
||||
private readonly ushort transactionId;
|
||||
|
||||
public byte Code { get { return wrapped.Code; } }
|
||||
public byte Slave { get { return wrapped.Slave; } }
|
||||
public ushort Address { get { return wrapped.Address; } }
|
||||
public IModbusCommand Wrapped { get { return wrapped; } }
|
||||
public ushort TransactionId { get { return transactionId; } }
|
||||
public int RequestLength { get { return wrapped.RequestLength + 6; } }
|
||||
public int ResponseLength { get { return wrapped.ResponseLength + 6; } }
|
||||
|
||||
public ModbusTCPWrapper(IModbusCommand wrapped, ushort transactionId)
|
||||
{
|
||||
this.wrapped = wrapped;
|
||||
this.transactionId = transactionId;
|
||||
}
|
||||
|
||||
public void FillRequest(byte[] request, int offset)
|
||||
{
|
||||
request[offset + 0] = ModbusUtils.High(transactionId);
|
||||
request[offset + 1] = ModbusUtils.Low(transactionId);
|
||||
request[offset + 2] = 0;
|
||||
request[offset + 3] = 0;
|
||||
request[offset + 4] = ModbusUtils.High(wrapped.RequestLength);
|
||||
request[offset + 5] = ModbusUtils.Low(wrapped.RequestLength);
|
||||
wrapped.FillRequest(request, offset + 6);
|
||||
}
|
||||
|
||||
public object ParseResponse(byte[] response, int offset)
|
||||
{
|
||||
Tools.AssertEqual(ModbusUtils.GetUShort(response, offset + 0), transactionId, "TransactionId mismatch got {0} expected {1}");
|
||||
Tools.AssertEqual(ModbusUtils.GetUShort(response, offset + 2), 0, "Zero mismatch got {0} expected {1}");
|
||||
Tools.AssertEqual(ModbusUtils.GetUShort(response, offset + 4), wrapped.ResponseLength, "Length mismatch got {0} expected {1}");
|
||||
return wrapped.ParseResponse(response, offset + 6);
|
||||
}
|
||||
|
||||
public object ApplyTo(IModbusModel model)
|
||||
{
|
||||
return wrapped.ApplyTo(model);
|
||||
}
|
||||
|
||||
public void FillResponse(byte[] response, int offset, object value)
|
||||
{
|
||||
response[offset + 0] = ModbusUtils.High(transactionId);
|
||||
response[offset + 1] = ModbusUtils.Low(transactionId);
|
||||
response[offset + 2] = 0;
|
||||
response[offset + 3] = 0;
|
||||
response[offset + 4] = ModbusUtils.High(wrapped.ResponseLength);
|
||||
response[offset + 5] = ModbusUtils.Low(wrapped.ResponseLength);
|
||||
wrapped.FillResponse(response, offset + 6, value);
|
||||
}
|
||||
|
||||
public byte[] GetException(byte code)
|
||||
{
|
||||
var exception = new byte[9];
|
||||
exception[0] = ModbusUtils.High(transactionId);
|
||||
exception[1] = ModbusUtils.Low(transactionId);
|
||||
exception[2] = 0;
|
||||
exception[3] = 0;
|
||||
exception[4] = ModbusUtils.High(3);
|
||||
exception[5] = ModbusUtils.Low(3);
|
||||
exception[6 + 0] = wrapped.Slave;
|
||||
exception[6 + 1] = (byte)(wrapped.Code | 0x80);
|
||||
exception[6 + 2] = code;
|
||||
return exception;
|
||||
}
|
||||
|
||||
public void CheckException(byte[] response, int count)
|
||||
{
|
||||
if (count < 9) Tools.Throw("Partial packet exception got {0} expected >= {1}", count, 9);
|
||||
var offset = 6;
|
||||
var code = response[offset + 1];
|
||||
if ((code & 0x80) != 0)
|
||||
{
|
||||
Tools.AssertEqual(response[offset + 0], wrapped.Slave, "Slave mismatch got {0} expected {1}");
|
||||
Tools.AssertEqual(code & 0x7F, wrapped.Code, "Code mismatch got {0} expected {1}");
|
||||
throw new ModbusException(response[offset + 2]);
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("[ModbusTCPWrapper Wrapped={0}, TransactionId={1}]", wrapped, transactionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
60
常用工具集/Utility/Network/Modbus/SharpModbus/ModbusTools.cs
Normal file
60
常用工具集/Utility/Network/Modbus/SharpModbus/ModbusTools.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace SharpModbus
|
||||
{
|
||||
public class SerialSettings : SharpSerial.SerialSettings { }
|
||||
|
||||
public static class Tools
|
||||
{
|
||||
public static void AssertEqual(int a, int b, string format)
|
||||
{
|
||||
if (a != b) Tools.Throw(format, a, b);
|
||||
}
|
||||
|
||||
public static TcpClient ConnectWithTimeout(string host, int port, int timeout)
|
||||
{
|
||||
var socket = new TcpClient();
|
||||
try
|
||||
{
|
||||
var result = socket.BeginConnect(host, port, null, null);
|
||||
var connected = result.AsyncWaitHandle.WaitOne(timeout, true);
|
||||
if (!connected) Tools.Throw("Timeout connecting to {0}:{1}", host, port);
|
||||
socket.EndConnect(result);
|
||||
return socket;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Tools.Dispose(socket);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Dispose(IDisposable disposable)
|
||||
{
|
||||
try { if (disposable != null) disposable.Dispose(); }
|
||||
catch (Exception) { }
|
||||
}
|
||||
|
||||
public static Exception Make(string format, params object[] args)
|
||||
{
|
||||
var message = format;
|
||||
if (args.Length > 0) message = string.Format(format, args);
|
||||
return new Exception(message);
|
||||
}
|
||||
|
||||
public static void Throw(string format, params object[] args)
|
||||
{
|
||||
var message = format;
|
||||
if (args.Length > 0) message = string.Format(format, args);
|
||||
throw new Exception(message);
|
||||
}
|
||||
|
||||
public static void Throw(Exception inner, string format, params object[] args)
|
||||
{
|
||||
var message = format;
|
||||
if (args.Length > 0) message = string.Format(format, args);
|
||||
throw new Exception(message, inner);
|
||||
}
|
||||
}
|
||||
}
|
||||
169
常用工具集/Utility/Network/Modbus/SharpModbus/ModbusUtils.cs
Normal file
169
常用工具集/Utility/Network/Modbus/SharpModbus/ModbusUtils.cs
Normal file
@@ -0,0 +1,169 @@
|
||||
using System;
|
||||
|
||||
namespace SharpModbus
|
||||
{
|
||||
public static class ModbusUtils
|
||||
{
|
||||
public static ushort CRC16(byte[] bytes, int offset, int count)
|
||||
{
|
||||
ushort crc = 0xFFFF;
|
||||
for (int pos = 0; pos < count; pos++)
|
||||
{
|
||||
crc ^= (ushort)bytes[pos + offset];
|
||||
for (int i = 8; i > 0; i--)
|
||||
{
|
||||
if ((crc & 0x0001) != 0)
|
||||
{
|
||||
crc >>= 1;
|
||||
crc ^= 0xA001;
|
||||
}
|
||||
else
|
||||
crc >>= 1;
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
public static byte EncodeBool(bool value)
|
||||
{
|
||||
return (byte)(value ? 0xFF : 0x00);
|
||||
}
|
||||
|
||||
public static bool DecodeBool(byte value)
|
||||
{
|
||||
return (value != 0x00);
|
||||
}
|
||||
|
||||
public static byte[] EncodeBools(bool[] bools)
|
||||
{
|
||||
var count = BytesForBools(bools.Length);
|
||||
var bytes = new byte[count];
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
bytes[i] = 0;
|
||||
}
|
||||
for (var i = 0; i < bools.Length; i++)
|
||||
{
|
||||
var v = bools[i];
|
||||
if (v)
|
||||
{
|
||||
var bi = i / 8;
|
||||
bytes[bi] |= (byte)(1 << (i % 8));
|
||||
}
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public static byte[] EncodeWords(ushort[] words)
|
||||
{
|
||||
var count = 2 * words.Length;
|
||||
var bytes = new byte[count];
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
bytes[i] = 0;
|
||||
}
|
||||
for (var i = 0; i < words.Length; i++)
|
||||
{
|
||||
bytes[2 * i + 0] = (byte)((words[i] >> 8) & 0xff);
|
||||
bytes[2 * i + 1] = (byte)((words[i] >> 0) & 0xff);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public static bool[] DecodeBools(byte[] packet, int offset, ushort count)
|
||||
{
|
||||
var bools = new bool[count];
|
||||
var bytes = BytesForBools(count);
|
||||
for (var i = 0; i < bytes; i++)
|
||||
{
|
||||
var bits = count >= 8 ? 8 : count % 8;
|
||||
var b = packet[offset + i];
|
||||
ByteToBools(b, bools, bools.Length - count, bits);
|
||||
count -= (ushort)bits;
|
||||
}
|
||||
return bools;
|
||||
}
|
||||
|
||||
public static ushort[] DecodeWords(byte[] packet, int offset, ushort count)
|
||||
{
|
||||
var results = new ushort[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
results[i] = (ushort)(packet[offset + 2 * i] << 8 | packet[offset + 2 * i + 1]);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private static void ByteToBools(byte b, bool[] bools, int offset, int count)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
bools[offset + i] = ((b >> i) & 0x01) == 1;
|
||||
}
|
||||
|
||||
public static byte BytesForWords(int count)
|
||||
{
|
||||
return (byte)(2 * count);
|
||||
}
|
||||
|
||||
public static byte BytesForBools(int count)
|
||||
{
|
||||
return (byte)(count == 0 ? 0 : (count - 1) / 8 + 1);
|
||||
}
|
||||
|
||||
public static byte High(int value)
|
||||
{
|
||||
return (byte)((value >> 8) & 0xff);
|
||||
}
|
||||
|
||||
public static byte Low(int value)
|
||||
{
|
||||
return (byte)((value >> 0) & 0xff);
|
||||
}
|
||||
|
||||
public static ushort GetUShort(byte bh, byte bl)
|
||||
{
|
||||
return (ushort)(
|
||||
((bh << 8) & 0xFF00)
|
||||
| (bl & 0xff)
|
||||
);
|
||||
}
|
||||
|
||||
public static ushort GetUShort(byte[] bytes, int offset)
|
||||
{
|
||||
return (ushort)(
|
||||
((bytes[offset + 0] << 8) & 0xFF00)
|
||||
| (bytes[offset + 1] & 0xff)
|
||||
);
|
||||
}
|
||||
|
||||
public static ushort GetUShortLittleEndian(byte[] bytes, int offset)
|
||||
{
|
||||
return (ushort)(
|
||||
((bytes[offset + 1] << 8) & 0xFF00)
|
||||
| (bytes[offset + 0] & 0xff)
|
||||
);
|
||||
}
|
||||
|
||||
public static void Copy(byte[] src, int srcOffset, byte[] dst, int dstOffset, int count)
|
||||
{
|
||||
for (var i = 0; i < count; i++)
|
||||
dst[dstOffset + i] = src[srcOffset + i];
|
||||
}
|
||||
|
||||
public static bool[] Clone(bool[] values)
|
||||
{
|
||||
var clone = new bool[values.Length];
|
||||
for (var i = 0; i < values.Length; i++)
|
||||
clone[i] = values[i];
|
||||
return clone;
|
||||
}
|
||||
|
||||
public static ushort[] Clone(ushort[] values)
|
||||
{
|
||||
var clone = new ushort[values.Length];
|
||||
for (var i = 0; i < values.Length; i++)
|
||||
clone[i] = values[i];
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user