Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Modbus
Project overview
Project overview
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
AM.WD
Modbus
Commits
cba58e5f
Commit
cba58e5f
authored
Mar 30, 2019
by
Andreas Müller
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Enhancement of clients to support function 43 (0x2B).
parent
1b12bd7d
Pipeline
#20
failed with stage
in 3 minutes and 12 seconds
Changes
22
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
22 changed files
with
1128 additions
and
116 deletions
+1128
-116
src/ConsoleDemo/ConsoleDemo.csproj
src/ConsoleDemo/ConsoleDemo.csproj
+2
-1
src/ConsoleDemo/Program.cs
src/ConsoleDemo/Program.cs
+164
-82
src/Modbus.Common/Enums.cs
src/Modbus.Common/Enums.cs
+92
-1
src/Modbus.Common/Interfaces/IModbusClient.cs
src/Modbus.Common/Interfaces/IModbusClient.cs
+18
-0
src/Modbus.Common/LICENSE.txt
src/Modbus.Common/LICENSE.txt
+21
-0
src/Modbus.Common/Modbus.Common.csproj
src/Modbus.Common/Modbus.Common.csproj
+1
-5
src/Modbus.Common/Util/DataBuffer.cs
src/Modbus.Common/Util/DataBuffer.cs
+5
-1
src/Modbus.Common/Util/Extensions.cs
src/Modbus.Common/Util/Extensions.cs
+7
-1
src/Modbus.Serial/Client/ModbusClient.cs
src/Modbus.Serial/Client/ModbusClient.cs
+95
-0
src/Modbus.Serial/LICENSE.txt
src/Modbus.Serial/LICENSE.txt
+21
-0
src/Modbus.Serial/Modbus.Serial.csproj
src/Modbus.Serial/Modbus.Serial.csproj
+2
-6
src/Modbus.Serial/Protocol/Request.cs
src/Modbus.Serial/Protocol/Request.cs
+65
-4
src/Modbus.Serial/Protocol/Response.cs
src/Modbus.Serial/Protocol/Response.cs
+86
-0
src/Modbus.Tcp/Client/ModbusClient.cs
src/Modbus.Tcp/Client/ModbusClient.cs
+110
-2
src/Modbus.Tcp/LICENSE.txt
src/Modbus.Tcp/LICENSE.txt
+21
-0
src/Modbus.Tcp/Modbus.Tcp.csproj
src/Modbus.Tcp/Modbus.Tcp.csproj
+1
-5
src/Modbus.Tcp/Protocol/Request.cs
src/Modbus.Tcp/Protocol/Request.cs
+67
-6
src/Modbus.Tcp/Protocol/Response.cs
src/Modbus.Tcp/Protocol/Response.cs
+86
-0
src/Modbus.Tcp/Server/ModbusServer.cs
src/Modbus.Tcp/Server/ModbusServer.cs
+120
-0
test/UnitTests/DataBufferTests.cs
test/UnitTests/DataBufferTests.cs
+28
-1
test/UnitTests/ModbusTcpTests.cs
test/UnitTests/ModbusTcpTests.cs
+115
-0
test/UnitTests/UnitTests.csproj
test/UnitTests/UnitTests.csproj
+1
-1
No files found.
src/ConsoleDemo/ConsoleDemo.csproj
View file @
cba58e5f
...
...
@@ -6,10 +6,11 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.
0.2
" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.
2.0
" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Modbus.Serial\Modbus.Serial.csproj" />
<ProjectReference Include="..\Modbus.Tcp\Modbus.Tcp.csproj" />
</ItemGroup>
...
...
src/ConsoleDemo/Program.cs
View file @
cba58e5f
using
AMWD.Modbus.Common.Structures
;
using
AMWD.Modbus.Common.Util
;
using
AMWD.Modbus.Tcp.Client
;
using
System
;
using
System.Collections.Generic
;
using
System.Linq
;
using
System.Text
;
using
System.Threading.Tasks
;
using
TcpClient
=
AMWD
.
Modbus
.
Tcp
.
Client
.
ModbusClient
;
using
SerialClient
=
AMWD
.
Modbus
.
Serial
.
Client
.
ModbusClient
;
using
AMWD.Modbus.Common.Interfaces
;
using
AMWD.Modbus.Common.Structures
;
using
AMWD.Modbus.Common.Util
;
using
AMWD.Modbus.Common
;
namespace
ConsoleDemo
{
...
...
@@ -14,99 +19,176 @@ namespace ConsoleDemo
private
static
void
Main
(
string
[]
args
)
{
Console
.
CancelKeyPress
+=
Console_CancelKeyPress
;
MainAsync
(
args
).
Wait
();
try
{
MainAsync
(
args
).
GetAwaiter
().
GetResult
();
}
catch
(
Exception
ex
)
{
Console
.
Error
.
WriteLine
(
ex
.
Message
);
}
}
private
static
async
Task
MainAsync
(
string
[]
args
)
{
Console
.
WriteLine
(
"Console Demo Modbus
TCP (Read Holding Registers)
"
);
Console
.
WriteLine
(
"Console Demo Modbus
Client
"
);
Console
.
WriteLine
();
Console
.
WriteLine
(
"Please enter the connection parameters:"
);
Console
.
Write
(
"Hostname : "
);
var
host
=
Console
.
ReadLine
();
Console
.
Write
(
"Port : "
);
var
port
=
Convert
.
ToInt32
(
Console
.
ReadLine
());
Console
.
Write
(
"Device ID: "
);
var
id
=
Convert
.
ToByte
(
Console
.
ReadLine
());
Console
.
Write
(
"Connection Type [1] TCP, [2] RS485: "
);
var
cType
=
Convert
.
ToInt32
(
Console
.
ReadLine
().
Trim
());
var
client
=
new
ModbusClient
(
host
,
port
);
while
(
run
)
IModbusClient
client
=
null
;
try
{
try
switch
(
cType
)
{
Console
.
WriteLine
();
Console
.
Write
(
"Address : "
);
var
address
=
Convert
.
ToUInt16
(
Console
.
ReadLine
()
);
Console
.
Write
(
"DataType: "
);
var
type
=
Console
.
ReadLine
(
);
Console
.
WriteLine
(
);
case
1
:
{
Console
.
Write
(
"Hostname: "
);
var
host
=
Console
.
ReadLine
().
Trim
(
);
Console
.
Write
(
"Port: "
);
var
port
=
Convert
.
ToInt32
(
Console
.
ReadLine
().
Trim
()
);
Console
.
Write
(
"Result : "
);
List
<
Register
>
result
=
null
;
switch
(
type
.
Trim
().
ToLower
())
{
case
"byte"
:
result
=
await
client
.
ReadHoldingRegisters
(
id
,
address
,
1
);
Console
.
WriteLine
(
result
.
First
().
GetByte
());
break
;
case
"ushort"
:
result
=
await
client
.
ReadHoldingRegisters
(
id
,
address
,
1
);
Console
.
WriteLine
(
result
.
First
().
GetUInt16
());
break
;
case
"uint"
:
result
=
await
client
.
ReadHoldingRegisters
(
id
,
address
,
2
);
Console
.
WriteLine
(
result
.
GetUInt32
());
break
;
case
"ulong"
:
result
=
await
client
.
ReadHoldingRegisters
(
id
,
address
,
4
);
Console
.
WriteLine
(
result
.
GetUInt64
());
break
;
case
"sbyte"
:
result
=
await
client
.
ReadHoldingRegisters
(
id
,
address
,
1
);
Console
.
WriteLine
(
result
.
First
().
GetSByte
());
break
;
case
"short"
:
result
=
await
client
.
ReadHoldingRegisters
(
id
,
address
,
1
);
Console
.
WriteLine
(
result
.
First
().
GetInt16
());
break
;
case
"int"
:
result
=
await
client
.
ReadHoldingRegisters
(
id
,
address
,
2
);
Console
.
WriteLine
(
result
.
GetInt32
());
break
;
case
"long"
:
result
=
await
client
.
ReadHoldingRegisters
(
id
,
address
,
4
);
Console
.
WriteLine
(
result
.
GetInt64
());
break
;
case
"float"
:
result
=
await
client
.
ReadHoldingRegisters
(
id
,
address
,
2
);
Console
.
WriteLine
(
result
.
GetSingle
());
break
;
case
"double"
:
result
=
await
client
.
ReadHoldingRegisters
(
id
,
address
,
4
);
Console
.
WriteLine
(
result
.
GetDouble
());
break
;
default
:
Console
.
Write
(
"DataType unknown"
);
break
;
}
client
=
new
TcpClient
(
host
,
port
);
}
break
;
case
2
:
{
Console
.
Write
(
"Interface: "
);
var
port
=
Console
.
ReadLine
().
Trim
();
client
=
new
SerialClient
(
port
);
}
break
;
default
:
throw
new
ArgumentException
(
"Type unknown"
);
}
catch
(
Exception
ex
)
await
client
.
Connect
();
while
(
run
)
{
Console
.
WriteLine
();
Console
.
WriteLine
(
"ERROR: "
+
ex
.
Message
);
Console
.
WriteLine
();
}
}
Console
.
Write
(
"Device ID: "
);
var
id
=
Convert
.
ToByte
(
Console
.
ReadLine
().
Trim
());
client
.
Dispose
(
);
}
Console
.
Write
(
"Function [1] Read Register, [2] Device Info: "
);
var
fn
=
Convert
.
ToInt32
(
Console
.
ReadLine
().
Trim
());
private
static
void
Console_CancelKeyPress
(
object
sender
,
ConsoleCancelEventArgs
e
)
{
run
=
false
;
try
{
switch
(
fn
)
{
case
1
:
{
ushort
address
=
0
;
ushort
count
=
0
;
string
type
=
""
;
Console
.
WriteLine
();
Console
.
Write
(
"Address : "
);
address
=
Convert
.
ToUInt16
(
Console
.
ReadLine
().
Trim
());
Console
.
Write
(
"DataType: "
);
type
=
Console
.
ReadLine
().
Trim
();
if
(
type
==
"string"
)
{
Console
.
Write
(
"Register Count: "
);
count
=
Convert
.
ToUInt16
(
Console
.
ReadLine
().
Trim
());
}
Console
.
WriteLine
();
Console
.
Write
(
"Result : "
);
List
<
Register
>
result
=
null
;
switch
(
type
.
Trim
().
ToLower
())
{
case
"byte"
:
result
=
await
client
.
ReadHoldingRegisters
(
id
,
address
,
1
);
Console
.
WriteLine
(
result
?.
First
().
GetByte
());
break
;
case
"ushort"
:
result
=
await
client
.
ReadHoldingRegisters
(
id
,
address
,
1
);
Console
.
WriteLine
(
result
?.
First
().
GetUInt16
());
break
;
case
"uint"
:
result
=
await
client
.
ReadHoldingRegisters
(
id
,
address
,
2
);
Console
.
WriteLine
(
result
?.
GetUInt32
());
break
;
case
"ulong"
:
result
=
await
client
.
ReadHoldingRegisters
(
id
,
address
,
4
);
Console
.
WriteLine
(
result
?.
GetUInt64
());
break
;
case
"sbyte"
:
result
=
await
client
.
ReadHoldingRegisters
(
id
,
address
,
1
);
Console
.
WriteLine
(
result
?.
First
().
GetSByte
());
break
;
case
"short"
:
result
=
await
client
.
ReadHoldingRegisters
(
id
,
address
,
1
);
Console
.
WriteLine
(
result
?.
First
().
GetInt16
());
break
;
case
"int"
:
result
=
await
client
.
ReadHoldingRegisters
(
id
,
address
,
2
);
Console
.
WriteLine
(
result
?.
GetInt32
());
break
;
case
"long"
:
result
=
await
client
.
ReadHoldingRegisters
(
id
,
address
,
4
);
Console
.
WriteLine
(
result
?.
GetInt64
());
break
;
case
"float"
:
result
=
await
client
.
ReadHoldingRegisters
(
id
,
address
,
2
);
Console
.
WriteLine
(
result
?.
GetSingle
());
break
;
case
"double"
:
result
=
await
client
.
ReadHoldingRegisters
(
id
,
address
,
4
);
Console
.
WriteLine
(
result
?.
GetDouble
());
break
;
case
"string"
:
result
=
await
client
.
ReadHoldingRegisters
(
id
,
address
,
count
);
Console
.
WriteLine
();
Console
.
WriteLine
(
"UTF8: "
+
result
?.
GetString
(
count
));
Console
.
WriteLine
(
"Unicode: "
+
result
?.
GetString
(
count
,
0
,
Encoding
.
Unicode
));
Console
.
WriteLine
(
"BigEndianUnicode: "
+
result
?.
GetString
(
count
,
0
,
Encoding
.
BigEndianUnicode
));
break
;
default
:
Console
.
Write
(
"DataType unknown"
);
break
;
}
}
break
;
case
2
:
{
var
info
=
await
client
.
ReadDeviceInformation
(
id
,
DeviceIDCategory
.
Regular
);
if
(
info
!=
null
)
{
foreach
(
var
kvp
in
info
)
{
Console
.
WriteLine
(
$"
{
kvp
.
Key
}
:
{
kvp
.
Value
}
"
);
}
}
}
break
;
}
}
catch
(
Exception
ex
)
{
Console
.
WriteLine
();
Console
.
WriteLine
(
"ERROR: "
+
ex
.
Message
);
}
Console
.
Write
(
"New Request? [y/N]: "
);
var
again
=
Console
.
ReadLine
().
Trim
().
ToLower
();
if
(
again
==
"y"
||
again
==
"yes"
||
again
==
"j"
||
again
==
"ja"
)
{
run
=
true
;
}
else
{
run
=
false
;
}
}
}
finally
{
client
?.
Dispose
();
}
}
}
}
src/Modbus.Common/Enums.cs
View file @
cba58e5f
...
...
@@ -69,7 +69,98 @@ namespace AMWD.Modbus.Common
/// Writes multiple registers (Fn 16).
/// </summary>
[
Description
(
"Write Multiple Registers"
)]
WriteMultipleRegisters
=
0x10
WriteMultipleRegisters
=
0x10
,
/// <summary>
/// Tunnels service requests and method invocations (Fn 43).
/// </summary>
/// <remarks>
/// This function code needs additional information about its type of request.
/// </remarks>
[
Description
(
"MODBUS Encapsulated Interface (MEI)"
)]
EncapsulatedInterface
=
0x2B
}
/// <summary>
/// Lists the possible MEI types.
/// </summary>
/// <remarks>
/// MEI = MODBUS Encapsulated Interface (Fn 43).
/// </remarks>
public
enum
MEIType
:
byte
{
/// <summary>
/// The request contains data of CANopen
/// </summary>
[
Description
(
"CANopen General Reference Request and Response PDU"
)]
CANOpenGeneralReference
=
0x0D
,
/// <summary>
/// The request contains data to read specific device information.
/// </summary>
[
Description
(
"Read Device Information"
)]
ReadDeviceInformation
=
0x0E
}
/// <summary>
/// Lists the category of the device information.
/// </summary>
public
enum
DeviceIDCategory
:
byte
{
/// <summary>
/// Read the basic information (mandatory).
/// </summary>
[
Description
(
"Basic Information Block"
)]
Basic
=
0x01
,
/// <summary>
/// Read the regular information (optional).
/// </summary>
[
Description
(
"Regular Information Block"
)]
Regular
=
0x02
,
/// <summary>
/// Read the extended information (optional, requires multiple requests).
/// </summary>
[
Description
(
"Extended Information Block"
)]
Extended
=
0x03
,
/// <summary>
/// Read an individual object.
/// </summary>
[
Description
(
"Individual Object"
)]
Individual
=
0x04
}
/// <summary>
/// List of known object ids of the device information.
/// </summary>
public
enum
DeviceIDObject
:
byte
{
/// <summary>
/// The vendor name (mandatory).
/// </summary>
VendorName
=
0x00
,
/// <summary>
/// The product code (mandatory).
/// </summary>
ProductCode
=
0x01
,
/// <summary>
/// The major and minor revision (mandatory).
/// </summary>
MajorMinorRevision
=
0x02
,
/// <summary>
/// The vendor url (optional).
/// </summary>
VendorUrl
=
0x03
,
/// <summary>
/// The product name (optional).
/// </summary>
ProductName
=
0x04
,
/// <summary>
/// The model name (optional).
/// </summary>
ModelName
=
0x05
,
/// <summary>
/// The application name (optional).
/// </summary>
UserApplicationName
=
0x06
}
/// <summary>
...
...
src/Modbus.Common/Interfaces/IModbusClient.cs
View file @
cba58e5f
...
...
@@ -93,6 +93,24 @@ namespace AMWD.Modbus.Common.Interfaces
/// <returns>A list of registers or null on error.</returns>
Task
<
List
<
Register
>>
ReadInputRegisters
(
byte
deviceId
,
ushort
startAddress
,
ushort
count
);
/// <summary>
/// Reads device information. (Modbus function 43).
/// </summary>
/// <param name="deviceId">The id to address the device (slave).</param>
/// <param name="categoryId">The category to read (basic, regular, extended, individual).</param>
/// <param name="objectId">The first object id to read.</param>
/// <returns>A map of device information and their content as string.</returns>
Task
<
Dictionary
<
DeviceIDObject
,
string
>>
ReadDeviceInformation
(
byte
deviceId
,
DeviceIDCategory
categoryId
,
DeviceIDObject
objectId
=
DeviceIDObject
.
VendorName
);
/// <summary>
/// Reads device information. (Modbus function 43).
/// </summary>
/// <param name="deviceId">The id to address the device (slave).</param>
/// <param name="categoryId">The category to read (basic, regular, extended, individual).</param>
/// <param name="objectId">The first object id to read.</param>
/// <returns>A map of device information and their content as raw bytes.</returns>
Task
<
Dictionary
<
byte
,
byte
[
]>
>
ReadDeviceInformationRaw
(
byte
deviceId
,
DeviceIDCategory
categoryId
,
DeviceIDObject
objectId
=
DeviceIDObject
.
VendorName
);
#
endregion
Read
methods
#
region
Write
methods
...
...
src/Modbus.Common/LICENSE.txt
0 → 100644
View file @
cba58e5f
The MIT License
Copyright (c) 2018 Andreas Müller
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.
\ No newline at end of file
src/Modbus.Common/Modbus.Common.csproj
View file @
cba58e5f
...
...
@@ -52,12 +52,8 @@
<DocumentationFile>$(OutputPath)\$(AssemblyName).xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.
0.2
" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.
2.0
" />
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.2.5" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<None Include="..\..\LICENSE.txt" Pack="true" PackagePath="" />
</ItemGroup>
</Project>
src/Modbus.Common/Util/DataBuffer.cs
View file @
cba58e5f
...
...
@@ -42,7 +42,11 @@ namespace AMWD.Modbus.Common.Util
/// <param name="bytes">New buffer as byte array.</param>
public
DataBuffer
(
byte
[]
bytes
)
{
Buffer
=
bytes
??
throw
new
ArgumentNullException
(
nameof
(
bytes
));
if
(
bytes
==
null
)
throw
new
ArgumentNullException
(
nameof
(
bytes
));
Buffer
=
new
byte
[
bytes
.
Length
];
Array
.
Copy
(
bytes
,
Buffer
,
bytes
.
Length
);
}
/// <summary>
...
...
src/Modbus.Common/Util/Extensions.cs
View file @
cba58e5f
...
...
@@ -249,7 +249,13 @@ namespace AMWD.Modbus.Common.Util
blob
[
i
*
2
+
1
]
=
registers
[
i
].
LoByte
;
}
return
encoding
.
GetString
(
blob
).
Trim
(
new
[]
{
' '
,
'\t'
,
'\0'
,
'\r'
,
'\n'
});
var
str
=
encoding
.
GetString
(
blob
).
Trim
(
new
[]
{
' '
,
'\t'
,
'\0'
,
'\r'
,
'\n'
});
var
nullIdx
=
str
.
IndexOf
(
'\0'
);
if
(
nullIdx
>=
0
)
{
return
str
.
Substring
(
0
,
nullIdx
);
}
return
str
;
}
#
endregion
To
string
...
...
src/Modbus.Serial/Client/ModbusClient.cs
View file @
cba58e5f
...
...
@@ -8,6 +8,7 @@ using System.Collections.Generic;
using
System.IO
;
using
System.IO.Ports
;
using
System.Linq
;
using
System.Text
;
using
System.Threading
;
using
System.Threading.Tasks
;
...
...
@@ -567,6 +568,100 @@ namespace AMWD.Modbus.Serial.Client
return
list
;
}
/// <summary>
/// Reads device information. (Modbus function 43).
/// </summary>
/// <param name="deviceId">The id to address the device (slave).</param>
/// <param name="categoryId">The category to read (basic, regular, extended, individual).</param>
/// <param name="objectId">The first object id to read.</param>
/// <returns>A map of device information and their content as string.</returns>
public
async
Task
<
Dictionary
<
DeviceIDObject
,
string
>>
ReadDeviceInformation
(
byte
deviceId
,
DeviceIDCategory
categoryId
,
DeviceIDObject
objectId
=
DeviceIDObject
.
VendorName
)
{
var
raw
=
await
ReadDeviceInformationRaw
(
deviceId
,
categoryId
,
objectId
);
if
(
raw
==
null
)
{
return
null
;
}
var
dict
=
new
Dictionary
<
DeviceIDObject
,
string
>();
foreach
(
var
kvp
in
raw
)
{
dict
.
Add
((
DeviceIDObject
)
kvp
.
Key
,
Encoding
.
ASCII
.
GetString
(
kvp
.
Value
));
}
return
dict
;
}
/// <summary>
/// Reads device information. (Modbus function 43).
/// </summary>
/// <param name="deviceId">The id to address the device (slave).</param>
/// <param name="categoryId">The category to read (basic, regular, extended, individual).</param>
/// <param name="objectId">The first object id to read.</param>
/// <returns>A map of device information and their content as raw bytes.</returns>>
public
async
Task
<
Dictionary
<
byte
,
byte
[
]>
>
ReadDeviceInformationRaw
(
byte
deviceId
,
DeviceIDCategory
categoryId
,
DeviceIDObject
objectId
=
DeviceIDObject
.
VendorName
)
{
if
(
isDisposed
)
{
throw
new
ObjectDisposedException
(
GetType
().
FullName
);
}
if
(
deviceId
<
Consts
.
MinDeviceId
||
Consts
.
MaxDeviceId
<
deviceId
)
{
throw
new
ArgumentOutOfRangeException
(
nameof
(
deviceId
));
}
try
{
var
request
=
new
Request
{
DeviceId
=
deviceId
,
Function
=
FunctionCode
.
EncapsulatedInterface
,
MEIType
=
MEIType
.
ReadDeviceInformation
,
MEICategory
=
categoryId
,
MEIObject
=
objectId
};
var
response
=
await
SendRequest
(
request
);
if
(
response
.
IsTimeout
)
{
throw
new
IOException
(
"Request timed out"
);
}
if
(
response
.
IsError
)
{
throw
new
ModbusException
(
response
.
ErrorMessage
);
}
var
dict
=
new
Dictionary
<
byte
,
byte
[
]>
();
for
(
int
i
=
0
,
idx
=
0
;
i
<
response
.
ObjectCount
&&
idx
<
response
.
Data
.
Length
;
i
++)
{
byte
objId
=
response
.
Data
.
GetByte
(
idx
);
idx
++;
byte
len
=
response
.
Data
.
GetByte
(
idx
);
idx
++;
byte
[]
bytes
=
response
.
Data
.
GetBytes
(
idx
,
len
);
idx
+=
len
;
dict
.
Add
(
objId
,
bytes
);
}
if
(
response
.
MoreRequestsNeeded
)
{
var
transDict
=
await
ReadDeviceInformationRaw
(
deviceId
,
categoryId
,
(
DeviceIDObject
)
response
.
NextObjectId
);
foreach
(
var
kvp
in
transDict
)
{
dict
.
Add
(
kvp
.
Key
,
kvp
.
Value
);
}
}
return
dict
;
}
catch
(
IOException
)
{
ConnectingTask
=
Task
.
Run
((
Action
)
Reconnect
);
}
return
null
;
}
#
endregion
Read
methods
#
region
Write
methods
...
...
src/Modbus.Serial/LICENSE.txt
0 → 100644
View file @
cba58e5f
The MIT License
Copyright (c) 2018 Andreas Müller
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.
\ No newline at end of file
src/Modbus.Serial/Modbus.Serial.csproj
View file @
cba58e5f
...
...
@@ -53,8 +53,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.
0.2
" />
<PackageReference Include="System.IO.Ports" Version="4.
5.0
" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.
2.0
" />
<PackageReference Include="System.IO.Ports" Version="4.
6.0-preview3.19128.7
" />
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.2.5" PrivateAssets="all" />
</ItemGroup>
...
...
@@ -62,8 +62,4 @@
<ProjectReference Include="..\Modbus.Common\Modbus.Common.csproj" />
</ItemGroup>
<ItemGroup>
<None Include="..\..\LICENSE.txt" Pack="true" PackagePath="" />
</ItemGroup>
</Project>
src/Modbus.Serial/Protocol/Request.cs
View file @
cba58e5f
...
...
@@ -68,6 +68,31 @@ namespace AMWD.Modbus.Serial.Protocol
/// </summary>
internal
DataBuffer
Data
{
get
;
set
;
}
#
region
MODBUS
Encapsulated
Interface
Transport
/// <summary>
/// Gets or sets the Encapsulated Interface type.
/// Only needed on <see cref="FunctionCode.EncapsulatedInterface"/>.
/// </summary>
public
MEIType
MEIType
{
get
;
set
;
}
#
region
Device
Information
/// <summary>
/// Gets or sets the Device ID code (category).
/// Only needed on <see cref="FunctionCode.EncapsulatedInterface"/> and <see cref="MEIType.ReadDeviceInformation"/>.
/// </summary>
public
DeviceIDCategory
MEICategory
{
get
;
set
;
}
/// <summary>
/// Gets or sets the first Object ID to read.
/// </summary>
public
DeviceIDObject
MEIObject
{
get
;
set
;
}
#
endregion
Device
Information
#
endregion
MODBUS
Encapsulated
Interface
Transport
#
endregion
Properties
#
region
Serialization
...
...
@@ -78,23 +103,23 @@ namespace AMWD.Modbus.Serial.Protocol
/// <returns></returns>
internal
byte
[]
Serialize
()
{
var
buffer
=
new
DataBuffer
(
4
);
var
buffer
=
new
DataBuffer
(
2
);
buffer
.
SetByte
(
0
,
DeviceId
);
buffer
.
SetByte
(
1
,
(
byte
)
Function
);
buffer
.
SetUInt16
(
2
,
Address
);
switch
(
Function
)
{
case
FunctionCode
.
ReadCoils
:
case
FunctionCode
.
ReadDiscreteInputs
:
case
FunctionCode
.
ReadHoldingRegisters
:
case
FunctionCode
.
ReadInputRegisters
:
buffer
.
AddUInt16
(
Address
);
buffer
.
AddUInt16
(
Count
);
break
;
case
FunctionCode
.
WriteMultipleCoils
:
case
FunctionCode
.
WriteMultipleRegisters
:
buffer
.
AddUInt16
(
Address
);
buffer
.
AddUInt16
(
Count
);
if
(
Data
?.
Length
>
0
)
{
...
...
@@ -103,11 +128,30 @@ namespace AMWD.Modbus.Serial.Protocol
break
;
case
FunctionCode
.
WriteSingleCoil
:
case
FunctionCode
.
WriteSingleRegister
:
buffer
.
AddUInt16
(
Address
);
if
(
Data
?.
Length
>
0
)
{
buffer
.
AddBytes
(
Data
.
Buffer
);
}
break
;
case
FunctionCode
.
EncapsulatedInterface
:
buffer
.
AddByte
((
byte
)
MEIType
);
switch
(
MEIType
)
{
case
MEIType
.
CANOpenGeneralReference
:
if
(
Data
?.
Length
>
0
)
{
buffer
.
AddBytes
(
Data
.
Buffer
);
}
break
;
case
MEIType
.
ReadDeviceInformation
:
buffer
.
AddByte
((
byte
)
MEICategory
);
buffer
.
AddByte
((
byte
)
MEIObject
);
break
;
default
:
throw
new
NotImplementedException
();
}
break
;
default
:
throw
new
NotImplementedException
();
}
...
...
@@ -123,7 +167,6 @@ namespace AMWD.Modbus.Serial.Protocol
var
buffer
=
new
DataBuffer
(
bytes
);