初始化上传
This commit is contained in:
211
常用工具集/Utility/Network/Modbus/EasyModbus/Exceptions/Exceptions.cs
Normal file
211
常用工具集/Utility/Network/Modbus/EasyModbus/Exceptions/Exceptions.cs
Normal file
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
Copyright (c) 2018-2020 Rossmann-Engineering
|
||||
Permission is hereby granted, free of charge,
|
||||
to any person obtaining a copy of this software
|
||||
and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction,
|
||||
including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit
|
||||
persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission
|
||||
notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace EasyModbus.Exceptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Exception to be thrown if serial port is not opened
|
||||
/// </summary>
|
||||
public class SerialPortNotOpenedException : ModbusException
|
||||
{
|
||||
public SerialPortNotOpenedException()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
public SerialPortNotOpenedException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public SerialPortNotOpenedException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
protected SerialPortNotOpenedException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exception to be thrown if Connection to Modbus device failed
|
||||
/// </summary>
|
||||
public class ConnectionException : ModbusException
|
||||
{
|
||||
public ConnectionException()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
public ConnectionException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public ConnectionException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
protected ConnectionException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exception to be thrown if Modbus Server returns error code "Function code not supported"
|
||||
/// </summary>
|
||||
public class FunctionCodeNotSupportedException : ModbusException
|
||||
{
|
||||
public FunctionCodeNotSupportedException()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
public FunctionCodeNotSupportedException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public FunctionCodeNotSupportedException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
protected FunctionCodeNotSupportedException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exception to be thrown if Modbus Server returns error code "quantity invalid"
|
||||
/// </summary>
|
||||
public class QuantityInvalidException : ModbusException
|
||||
{
|
||||
public QuantityInvalidException()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
public QuantityInvalidException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public QuantityInvalidException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
protected QuantityInvalidException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exception to be thrown if Modbus Server returns error code "starting adddress and quantity invalid"
|
||||
/// </summary>
|
||||
public class StartingAddressInvalidException : ModbusException
|
||||
{
|
||||
public StartingAddressInvalidException()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
public StartingAddressInvalidException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public StartingAddressInvalidException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
protected StartingAddressInvalidException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exception to be thrown if Modbus Server returns error code "Function Code not executed (0x04)"
|
||||
/// </summary>
|
||||
public class ModbusException : Exception
|
||||
{
|
||||
public ModbusException()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
public ModbusException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public ModbusException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
protected ModbusException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exception to be thrown if CRC Check failed
|
||||
/// </summary>
|
||||
public class CRCCheckFailedException : ModbusException
|
||||
{
|
||||
public CRCCheckFailedException()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
public CRCCheckFailedException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public CRCCheckFailedException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
protected CRCCheckFailedException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
2878
常用工具集/Utility/Network/Modbus/EasyModbus/ModbusClient.cs
Normal file
2878
常用工具集/Utility/Network/Modbus/EasyModbus/ModbusClient.cs
Normal file
File diff suppressed because it is too large
Load Diff
2277
常用工具集/Utility/Network/Modbus/EasyModbus/ModbusServer.cs
Normal file
2277
常用工具集/Utility/Network/Modbus/EasyModbus/ModbusServer.cs
Normal file
File diff suppressed because it is too large
Load Diff
120
常用工具集/Utility/Network/Modbus/EasyModbus/StoreLogData.cs
Normal file
120
常用工具集/Utility/Network/Modbus/EasyModbus/StoreLogData.cs
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
Copyright (c) 2018-2020 Rossmann-Engineering
|
||||
Permission is hereby granted, free of charge,
|
||||
to any person obtaining a copy of this software
|
||||
and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction,
|
||||
including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit
|
||||
persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission
|
||||
notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace EasyModbus
|
||||
{
|
||||
/// <summary>
|
||||
/// Store Log-Data in a File
|
||||
/// </summary>
|
||||
public sealed class StoreLogData
|
||||
{
|
||||
private String filename = null;
|
||||
private static volatile StoreLogData instance;
|
||||
private static object syncObject = new Object();
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor; Ensures the access of the class only via "instance"
|
||||
/// </summary>
|
||||
private StoreLogData()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the instance of the class (singleton)
|
||||
/// </summary>
|
||||
/// <returns>instance (Singleton)</returns>
|
||||
public static StoreLogData Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
lock (syncObject)
|
||||
{
|
||||
if (instance == null)
|
||||
instance = new StoreLogData();
|
||||
}
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Store message in Log-File
|
||||
/// </summary>
|
||||
/// <param name="message">Message to append to the Log-File</param>
|
||||
public void Store(String message)
|
||||
{
|
||||
if (this.filename == null)
|
||||
return;
|
||||
|
||||
using (System.IO.StreamWriter file =
|
||||
new System.IO.StreamWriter(Filename, true))
|
||||
{
|
||||
file.WriteLine(message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Store message in Log-File including Timestamp
|
||||
/// </summary>
|
||||
/// <param name="message">Message to append to the Log-File</param>
|
||||
/// <param name="timestamp">Timestamp to add to the same Row</param>
|
||||
public void Store(String message, DateTime timestamp)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (System.IO.StreamWriter file =
|
||||
new System.IO.StreamWriter(Filename, true))
|
||||
{
|
||||
file.WriteLine(timestamp.ToString("dd.MM.yyyy H:mm:ss.ff ") + message);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets the Filename to Store Strings in a File
|
||||
/// </summary>
|
||||
public string Filename
|
||||
{
|
||||
get
|
||||
{
|
||||
return filename;
|
||||
}
|
||||
set
|
||||
{
|
||||
filename = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1263
常用工具集/Utility/Network/Modbus/ModbusHelper.cs
Normal file
1263
常用工具集/Utility/Network/Modbus/ModbusHelper.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
47
常用工具集/Utility/Network/Modbus/SharpSerial/Program.Stdio.cs
Normal file
47
常用工具集/Utility/Network/Modbus/SharpSerial/Program.Stdio.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace SharpSerial
|
||||
{
|
||||
public static class Stdio
|
||||
{
|
||||
private static bool traceTimed = false;
|
||||
private static bool traceEnabled = false;
|
||||
private static readonly object locker = new object();
|
||||
|
||||
public static string ReadLine() => Console.ReadLine();
|
||||
|
||||
public static void WriteLine(string format, params object[] args)
|
||||
{
|
||||
lock (locker)
|
||||
{
|
||||
Console.Out.WriteLine(format, args);
|
||||
Console.Out.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
public static void Trace(string format, params object[] args)
|
||||
{
|
||||
if (traceEnabled)
|
||||
{
|
||||
lock (locker)
|
||||
{
|
||||
if (traceTimed)
|
||||
{
|
||||
Console.Error.Write(DateTime.Now.ToString("HH:mm:ss.fff"));
|
||||
Console.Error.Write(" ");
|
||||
}
|
||||
Console.Error.WriteLine(format, args);
|
||||
Console.Error.Flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void EnableTrace(bool enable, bool timed)
|
||||
{
|
||||
traceTimed = timed;
|
||||
traceEnabled = enable;
|
||||
}
|
||||
}
|
||||
}
|
||||
99
常用工具集/Utility/Network/Modbus/SharpSerial/Program.Tools.cs
Normal file
99
常用工具集/Utility/Network/Modbus/SharpSerial/Program.Tools.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace SharpSerial
|
||||
{
|
||||
public static class Tools
|
||||
{
|
||||
public static void SetupGlobalCatcher()
|
||||
{
|
||||
AppDomain.CurrentDomain.UnhandledException += ExceptionHandler;
|
||||
}
|
||||
|
||||
public static void ExceptionHandler(object sender, UnhandledExceptionEventArgs args)
|
||||
{
|
||||
ExceptionHandler(args.ExceptionObject as Exception);
|
||||
}
|
||||
|
||||
public static void ExceptionHandler(Exception ex)
|
||||
{
|
||||
Try(() => Stdio.Trace("!{0}", ex.ToString()));
|
||||
Try(() => Stdio.WriteLine("!{0}", ex.ToString()));
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
public static string StringHex(byte[] data)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append("<");
|
||||
foreach (var b in data) sb.Append(b.ToString("X2"));
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static byte[] ParseHex(string text)
|
||||
{
|
||||
Assert(text.Length % 2 == 1, "Odd length expected for {0}:{1}", text.Length, text);
|
||||
var bytes = new byte[text.Length / 2];
|
||||
for (var i = 0; i < bytes.Length; i++)
|
||||
{
|
||||
var b2 = text.Substring(1 + i * 2, 2);
|
||||
bytes[i] = Convert.ToByte(b2, 16);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public static void SetProperty(object target, string line)
|
||||
{
|
||||
var parts = line.Split(new char[] { '=' });
|
||||
if (parts.Length != 2) throw Make("Expected 2 parts in {0}", Readable(line));
|
||||
var propertyName = parts[0];
|
||||
var propertyValue = parts[1];
|
||||
var property = target.GetType().GetProperty(propertyName);
|
||||
if (property == null) throw Make("Property not found {0}", Readable(propertyName));
|
||||
var value = FromString(property.PropertyType, propertyValue);
|
||||
property.SetValue(target, value, null);
|
||||
}
|
||||
|
||||
public static object FromString(Type type, string text)
|
||||
{
|
||||
if (type.IsEnum) return Enum.Parse(type, text);
|
||||
return Convert.ChangeType(text, type);
|
||||
}
|
||||
|
||||
public static void Try(Action action)
|
||||
{
|
||||
try
|
||||
{
|
||||
action.Invoke();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
//no clear use case for cleanup exception
|
||||
}
|
||||
}
|
||||
|
||||
public static Exception Make(string format, params object[] args)
|
||||
{
|
||||
var line = format;
|
||||
if (args.Length > 0) line = string.Format(format, args);
|
||||
return new Exception(line);
|
||||
}
|
||||
|
||||
public static string Readable(string text)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
foreach (var c in text)
|
||||
{
|
||||
if (Char.IsControl(c)) sb.Append(((int)c).ToString("X2"));
|
||||
else if (Char.IsWhiteSpace(c)) sb.Append(((int)c).ToString("X2"));
|
||||
else sb.Append(c);
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static void Assert(bool condition, string format, params object[] args)
|
||||
{
|
||||
if (!condition) throw Make(format, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
97
常用工具集/Utility/Network/Modbus/SharpSerial/SerialDevice.cs
Normal file
97
常用工具集/Utility/Network/Modbus/SharpSerial/SerialDevice.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
using System.IO.Ports;
|
||||
|
||||
namespace SharpSerial
|
||||
{
|
||||
// com0com BytesToRead is UNRELIABLE
|
||||
// Solution based on BaseStream and influenced by
|
||||
// https://www.sparxeng.com/blog/software/must-use-net-system-io-ports-serialport
|
||||
// https://www.vgies.com/a-reliable-serial-port-in-c/
|
||||
public class SerialDevice : ISerialStream, IDisposable
|
||||
{
|
||||
private readonly SerialPort serial;
|
||||
private readonly List<byte> list;
|
||||
private readonly Queue<byte> queue;
|
||||
private readonly byte[] buffer;
|
||||
|
||||
public SerialDevice(object settings)
|
||||
{
|
||||
this.buffer = new byte[256];
|
||||
this.list = new List<byte>(256);
|
||||
this.queue = new Queue<byte>(256);
|
||||
this.serial = new SerialPort();
|
||||
|
||||
//init serial port and launch async reader
|
||||
SerialSettings.CopyProperties(settings, serial);
|
||||
serial.Open();
|
||||
//DiscardInBuffer not needed by FTDI and ignored by com0com
|
||||
var stream = serial.BaseStream;
|
||||
//unavailable after closed so pass it
|
||||
stream.BeginRead(buffer, 0, buffer.Length, ReadCallback, stream);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Tools.Try(serial.Close);
|
||||
Tools.Try(serial.Dispose);
|
||||
}
|
||||
|
||||
public void Write(byte[] data)
|
||||
{
|
||||
var stream = serial.BaseStream;
|
||||
stream.Write(data, 0, data.Length);
|
||||
//always flush to allow sync by following read available
|
||||
stream.Flush();
|
||||
}
|
||||
|
||||
public byte[] Read(int size, int eop, int toms)
|
||||
{
|
||||
list.Clear();
|
||||
var dl = DateTime.Now.AddMilliseconds(toms);
|
||||
while (true)
|
||||
{
|
||||
int b = ReadByte();
|
||||
if (b == -1)
|
||||
{
|
||||
//toms=0 should return immediately with available
|
||||
if (DateTime.Now >= dl) break;
|
||||
Thread.Sleep(1);
|
||||
continue;
|
||||
}
|
||||
list.Add((byte)b);
|
||||
if (eop >= 0 && b == eop) break;
|
||||
if (size >= 0 && list.Count >= size) break;
|
||||
dl = DateTime.Now.AddMilliseconds(toms);
|
||||
}
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
private int ReadByte()
|
||||
{
|
||||
lock (queue)
|
||||
{
|
||||
if (queue.Count == 0) return -1;
|
||||
return queue.Dequeue();
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadCallback(IAsyncResult ar)
|
||||
{
|
||||
Tools.Try(() =>
|
||||
{
|
||||
//try needed to avoid triggering the domain unhandled
|
||||
//exception handler when used as standalone stream
|
||||
var stream = ar.AsyncState as Stream;
|
||||
int count = stream.EndRead(ar);
|
||||
if (count > 0) //0 for closed stream
|
||||
{
|
||||
lock (queue) for (var i = 0; i < count; i++) queue.Enqueue(buffer[i]);
|
||||
stream.BeginRead(buffer, 0, buffer.Length, ReadCallback, stream);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
16
常用工具集/Utility/Network/Modbus/SharpSerial/SerialException.cs
Normal file
16
常用工具集/Utility/Network/Modbus/SharpSerial/SerialException.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace SharpSerial
|
||||
{
|
||||
public class SerialException : Exception
|
||||
{
|
||||
private readonly string trace;
|
||||
|
||||
public SerialException(string message, string trace) : base(message)
|
||||
{
|
||||
this.trace = trace;
|
||||
}
|
||||
|
||||
public string Trace { get { return trace; } }
|
||||
}
|
||||
}
|
||||
126
常用工具集/Utility/Network/Modbus/SharpSerial/SerialProcess.cs
Normal file
126
常用工具集/Utility/Network/Modbus/SharpSerial/SerialProcess.cs
Normal file
@@ -0,0 +1,126 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharpSerial
|
||||
{
|
||||
//this class should not swallow exceptions outside dispose
|
||||
public class SerialProcess : ISerialStream, IDisposable
|
||||
{
|
||||
private readonly Process process;
|
||||
private readonly int pid;
|
||||
|
||||
public int Pid { get { return pid; } }
|
||||
|
||||
public SerialProcess(object settings)
|
||||
{
|
||||
var ss = new SerialSettings(settings);
|
||||
var args = new StringBuilder();
|
||||
foreach (var p in ss.GetType().GetProperties())
|
||||
{
|
||||
if (args.Length > 0) args.Append(" ");
|
||||
args.AppendFormat("{0}={1}", p.Name, p.GetValue(ss, null).ToString());
|
||||
}
|
||||
process = new Process();
|
||||
process.StartInfo = new ProcessStartInfo()
|
||||
{
|
||||
FileName = typeof(SerialProcess).Assembly.Location,
|
||||
Arguments = args.ToString(),
|
||||
CreateNoWindow = true,
|
||||
UseShellExecute = false,
|
||||
RedirectStandardInput = true,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = false,
|
||||
};
|
||||
EnableStandardError(process.StartInfo);
|
||||
process.Start();
|
||||
pid = process.Id;
|
||||
ForwardStandardError(process.StandardError);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Tools.Try(() =>
|
||||
{
|
||||
process.StandardInput.Close();
|
||||
process.WaitForExit(200);
|
||||
});
|
||||
Tools.Try(process.Kill);
|
||||
Tools.Try(process.Dispose);
|
||||
}
|
||||
|
||||
public void Write(byte[] data)
|
||||
{
|
||||
WriteHex(data);
|
||||
var line = ReadLine();
|
||||
Tools.Assert(line == "<ok", "Unexpected write response {0}", line);
|
||||
}
|
||||
|
||||
public byte[] Read(int size, int eop, int toms)
|
||||
{
|
||||
WriteLine("$r,{0},{1},{2}", size, eop, toms);
|
||||
return ParseHex(ReadLine());
|
||||
}
|
||||
|
||||
private string ReadLine()
|
||||
{
|
||||
var line = process.StandardOutput.ReadLine();
|
||||
if (line == null) throw new EndOfStreamException("Serial process EOF");
|
||||
if (line.StartsWith("!"))
|
||||
{
|
||||
var trace = process.StandardOutput.ReadToEnd();
|
||||
throw new SerialException(line.Substring(1), trace);
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
private void WriteLine(string format, params object[] args)
|
||||
{
|
||||
process.StandardInput.WriteLine(format, args);
|
||||
process.StandardInput.Flush();
|
||||
}
|
||||
|
||||
private void WriteHex(byte[] data)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(">");
|
||||
foreach (var b in data) sb.Append(b.ToString("X2"));
|
||||
WriteLine(sb.ToString());
|
||||
}
|
||||
|
||||
private byte[] ParseHex(string text)
|
||||
{
|
||||
Tools.Assert(text.StartsWith("<"), "First char < expected for {0}:{1}", text.Length, text);
|
||||
Tools.Assert(text.Length % 2 == 1, "Odd length expected for {0}:{1}", text.Length, text);
|
||||
var bytes = new byte[text.Length / 2];
|
||||
for (var i = 0; i < bytes.Length; i++)
|
||||
{
|
||||
var b2 = text.Substring(1 + i * 2, 2);
|
||||
bytes[i] = Convert.ToByte(b2, 16);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
private void EnableStandardError(ProcessStartInfo psi)
|
||||
{
|
||||
psi.RedirectStandardError = true;
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
private void ForwardStandardError(StreamReader reader)
|
||||
{
|
||||
Task.Factory.StartNew(() =>
|
||||
{
|
||||
var line = reader.ReadLine();
|
||||
while (line != null)
|
||||
{
|
||||
Stdio.Trace(line);
|
||||
line = reader.ReadLine();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
141
常用工具集/Utility/Network/Modbus/SharpSerial/SerialSettings.cs
Normal file
141
常用工具集/Utility/Network/Modbus/SharpSerial/SerialSettings.cs
Normal file
@@ -0,0 +1,141 @@
|
||||
using System;
|
||||
using System.IO.Ports;
|
||||
using System.Globalization;
|
||||
using System.ComponentModel;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace SharpSerial
|
||||
{
|
||||
public class SerialSettings
|
||||
{
|
||||
public SerialSettings() => CopyProperties(new SerialPort(), this);
|
||||
public SerialSettings(string portName) => CopyProperties(new SerialPort(portName), this);
|
||||
public SerialSettings(object source) => CopyProperties(source, this);
|
||||
|
||||
public void CopyFrom(object source) => CopyProperties(source, this);
|
||||
public void CopyTo(object target) => CopyProperties(this, target);
|
||||
|
||||
[TypeConverter(typeof(PortNameConverter))]
|
||||
public string PortName { get; set; }
|
||||
[TypeConverter(typeof(BaudRateConverter))]
|
||||
public int BaudRate { get; set; }
|
||||
public int DataBits { get; set; }
|
||||
public Parity Parity { get; set; }
|
||||
public StopBits StopBits { get; set; }
|
||||
public Handshake Handshake { get; set; }
|
||||
|
||||
public static void CopyProperties(Object source, Object target)
|
||||
{
|
||||
CopyProperty(source, target, "PortName");
|
||||
CopyProperty(source, target, "BaudRate");
|
||||
CopyProperty(source, target, "DataBits");
|
||||
CopyProperty(source, target, "Parity");
|
||||
CopyProperty(source, target, "StopBits");
|
||||
CopyProperty(source, target, "Handshake");
|
||||
}
|
||||
|
||||
static void CopyProperty(Object source, Object target, string name)
|
||||
{
|
||||
var propertySource = source.GetType().GetProperty(name);
|
||||
var propertyTarget = target.GetType().GetProperty(name);
|
||||
propertyTarget.SetValue(target, propertySource.GetValue(source, null), null);
|
||||
}
|
||||
}
|
||||
|
||||
public class PortNameConverter : TypeConverter
|
||||
{
|
||||
//windows only?
|
||||
private readonly Regex re = new Regex(@"[^a-zA-Z0-9_]");
|
||||
|
||||
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
|
||||
{
|
||||
return new StandardValuesCollection(SerialPort.GetPortNames());
|
||||
}
|
||||
|
||||
public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
return (sourceType == typeof(string));
|
||||
}
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext context,
|
||||
CultureInfo culture, object value)
|
||||
{
|
||||
if (re.IsMatch(value.ToString())) throw Tools.Make("Invalid chars");
|
||||
return value;
|
||||
}
|
||||
|
||||
public override object ConvertTo(ITypeDescriptorContext context,
|
||||
CultureInfo culture, object value, Type destinationType)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public class BaudRateConverter : TypeConverter
|
||||
{
|
||||
public readonly static int[] BaudRates = new int[] {
|
||||
110,
|
||||
300,
|
||||
600,
|
||||
1200,
|
||||
2400,
|
||||
4800,
|
||||
9600,
|
||||
14400,
|
||||
19200,
|
||||
28800,
|
||||
38400,
|
||||
56000,
|
||||
57600,
|
||||
115200,
|
||||
128000,
|
||||
153600,
|
||||
230400,
|
||||
256000,
|
||||
460800,
|
||||
921600
|
||||
};
|
||||
|
||||
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
|
||||
{
|
||||
return new StandardValuesCollection(BaudRates);
|
||||
}
|
||||
|
||||
public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
return (sourceType == typeof(string));
|
||||
}
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext context,
|
||||
CultureInfo culture, object value)
|
||||
{
|
||||
return int.Parse(value.ToString());
|
||||
}
|
||||
|
||||
public override object ConvertTo(ITypeDescriptorContext context,
|
||||
CultureInfo culture, object value, Type destinationType)
|
||||
{
|
||||
return value.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
10
常用工具集/Utility/Network/Modbus/SharpSerial/SerialStream.cs
Normal file
10
常用工具集/Utility/Network/Modbus/SharpSerial/SerialStream.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace SharpSerial
|
||||
{
|
||||
public interface ISerialStream : IDisposable
|
||||
{
|
||||
void Write(byte[] data);
|
||||
byte[] Read(int size, int eop, int toms);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user