├── .gitignore
├── Karonda.ModbusTcp.Client
├── Karonda.ModbusTcp.Client.csproj
└── Program.cs
├── Karonda.ModbusTcp.Server
├── Karonda.ModbusTcp.Server.csproj
├── ModbusResponse.cs
└── Program.cs
├── Karonda.ModbusTcp.sln
├── Karonda.ModbusTcp
├── Entity
│ ├── Function
│ │ ├── ExceptionFunction.cs
│ │ ├── ReadCoilsInputsResponse.cs
│ │ ├── ReadRegistersResponse.cs
│ │ ├── ReadWriteMultiple.cs
│ │ ├── Request
│ │ │ ├── ReadCoilsRequest.cs
│ │ │ ├── ReadDiscreteInputsRequest.cs
│ │ │ ├── ReadHoldingRegistersRequest.cs
│ │ │ ├── ReadInputRegistersRequest.cs
│ │ │ ├── WriteMultipleCoilsRequest.cs
│ │ │ ├── WriteMultipleRegistersRequest.cs
│ │ │ ├── WriteSingleCoilRequest.cs
│ │ │ └── WriteSingleRegisterRequest.cs
│ │ ├── Response
│ │ │ ├── ReadCoilsResponse.cs
│ │ │ ├── ReadDiscreteInputsResponse.cs
│ │ │ ├── ReadHoldingRegistersResponse.cs
│ │ │ ├── ReadInputRegistersResponse.cs
│ │ │ ├── WriteMultipleCoilsResponse.cs
│ │ │ ├── WriteMultipleRegistersResponse.cs
│ │ │ ├── WriteSingleCoilResponse.cs
│ │ │ └── WriteSingleRegisterResponse.cs
│ │ ├── WriteSingle.cs
│ │ └── WriteSingleCoil.cs
│ ├── ModbusCommand.cs
│ ├── ModbusFrame.cs
│ ├── ModbusFunction.cs
│ └── ModbusHeader.cs
├── Handler
│ ├── ModbusDecoder.cs
│ ├── ModbusEncoder.cs
│ ├── ModbusRequestHandler.cs
│ └── ModbusResponseHandler.cs
├── Karonda.ModbusTcp.csproj
├── ModbusClient.cs
├── ModbusFunctionClassDiagram.cd
├── ModbusFunctionClassDiagram.png
├── ModbusResponseService.cs
└── ModbusServer.cs
├── LICENSE
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | [Aa][Rr][Mm]/
24 | [Aa][Rr][Mm]64/
25 | bld/
26 | [Bb]in/
27 | [Oo]bj/
28 | [Ll]og/
29 |
30 | # Visual Studio 2015/2017 cache/options directory
31 | .vs/
32 | # Uncomment if you have tasks that create the project's static files in wwwroot
33 | #wwwroot/
34 |
35 | # Visual Studio 2017 auto generated files
36 | Generated\ Files/
37 |
38 | # MSTest test Results
39 | [Tt]est[Rr]esult*/
40 | [Bb]uild[Ll]og.*
41 |
42 | # NUNIT
43 | *.VisualState.xml
44 | TestResult.xml
45 |
46 | # Build Results of an ATL Project
47 | [Dd]ebugPS/
48 | [Rr]eleasePS/
49 | dlldata.c
50 |
51 | # Benchmark Results
52 | BenchmarkDotNet.Artifacts/
53 |
54 | # .NET Core
55 | project.lock.json
56 | project.fragment.lock.json
57 | artifacts/
58 |
59 | # StyleCop
60 | StyleCopReport.xml
61 |
62 | # Files built by Visual Studio
63 | *_i.c
64 | *_p.c
65 | *_h.h
66 | *.ilk
67 | *.meta
68 | *.obj
69 | *.iobj
70 | *.pch
71 | *.pdb
72 | *.ipdb
73 | *.pgc
74 | *.pgd
75 | *.rsp
76 | *.sbr
77 | *.tlb
78 | *.tli
79 | *.tlh
80 | *.tmp
81 | *.tmp_proj
82 | *_wpftmp.csproj
83 | *.log
84 | *.vspscc
85 | *.vssscc
86 | .builds
87 | *.pidb
88 | *.svclog
89 | *.scc
90 |
91 | # Chutzpah Test files
92 | _Chutzpah*
93 |
94 | # Visual C++ cache files
95 | ipch/
96 | *.aps
97 | *.ncb
98 | *.opendb
99 | *.opensdf
100 | *.sdf
101 | *.cachefile
102 | *.VC.db
103 | *.VC.VC.opendb
104 |
105 | # Visual Studio profiler
106 | *.psess
107 | *.vsp
108 | *.vspx
109 | *.sap
110 |
111 | # Visual Studio Trace Files
112 | *.e2e
113 |
114 | # TFS 2012 Local Workspace
115 | $tf/
116 |
117 | # Guidance Automation Toolkit
118 | *.gpState
119 |
120 | # ReSharper is a .NET coding add-in
121 | _ReSharper*/
122 | *.[Rr]e[Ss]harper
123 | *.DotSettings.user
124 |
125 | # JustCode is a .NET coding add-in
126 | .JustCode
127 |
128 | # TeamCity is a build add-in
129 | _TeamCity*
130 |
131 | # DotCover is a Code Coverage Tool
132 | *.dotCover
133 |
134 | # AxoCover is a Code Coverage Tool
135 | .axoCover/*
136 | !.axoCover/settings.json
137 |
138 | # Visual Studio code coverage results
139 | *.coverage
140 | *.coveragexml
141 |
142 | # NCrunch
143 | _NCrunch_*
144 | .*crunch*.local.xml
145 | nCrunchTemp_*
146 |
147 | # MightyMoose
148 | *.mm.*
149 | AutoTest.Net/
150 |
151 | # Web workbench (sass)
152 | .sass-cache/
153 |
154 | # Installshield output folder
155 | [Ee]xpress/
156 |
157 | # DocProject is a documentation generator add-in
158 | DocProject/buildhelp/
159 | DocProject/Help/*.HxT
160 | DocProject/Help/*.HxC
161 | DocProject/Help/*.hhc
162 | DocProject/Help/*.hhk
163 | DocProject/Help/*.hhp
164 | DocProject/Help/Html2
165 | DocProject/Help/html
166 |
167 | # Click-Once directory
168 | publish/
169 |
170 | # Publish Web Output
171 | *.[Pp]ublish.xml
172 | *.azurePubxml
173 | # Note: Comment the next line if you want to checkin your web deploy settings,
174 | # but database connection strings (with potential passwords) will be unencrypted
175 | *.pubxml
176 | *.publishproj
177 |
178 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
179 | # checkin your Azure Web App publish settings, but sensitive information contained
180 | # in these scripts will be unencrypted
181 | PublishScripts/
182 |
183 | # NuGet Packages
184 | *.nupkg
185 | # The packages folder can be ignored because of Package Restore
186 | **/[Pp]ackages/*
187 | # except build/, which is used as an MSBuild target.
188 | !**/[Pp]ackages/build/
189 | # Uncomment if necessary however generally it will be regenerated when needed
190 | #!**/[Pp]ackages/repositories.config
191 | # NuGet v3's project.json files produces more ignorable files
192 | *.nuget.props
193 | *.nuget.targets
194 |
195 | # Microsoft Azure Build Output
196 | csx/
197 | *.build.csdef
198 |
199 | # Microsoft Azure Emulator
200 | ecf/
201 | rcf/
202 |
203 | # Windows Store app package directories and files
204 | AppPackages/
205 | BundleArtifacts/
206 | Package.StoreAssociation.xml
207 | _pkginfo.txt
208 | *.appx
209 |
210 | # Visual Studio cache files
211 | # files ending in .cache can be ignored
212 | *.[Cc]ache
213 | # but keep track of directories ending in .cache
214 | !?*.[Cc]ache/
215 |
216 | # Others
217 | ClientBin/
218 | ~$*
219 | *~
220 | *.dbmdl
221 | *.dbproj.schemaview
222 | *.jfm
223 | *.pfx
224 | *.publishsettings
225 | orleans.codegen.cs
226 |
227 | # Including strong name files can present a security risk
228 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
229 | #*.snk
230 |
231 | # Since there are multiple workflows, uncomment next line to ignore bower_components
232 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
233 | #bower_components/
234 | # ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true
235 | **/wwwroot/lib/
236 |
237 | # RIA/Silverlight projects
238 | Generated_Code/
239 |
240 | # Backup & report files from converting an old project file
241 | # to a newer Visual Studio version. Backup files are not needed,
242 | # because we have git ;-)
243 | _UpgradeReport_Files/
244 | Backup*/
245 | UpgradeLog*.XML
246 | UpgradeLog*.htm
247 | ServiceFabricBackup/
248 | *.rptproj.bak
249 |
250 | # SQL Server files
251 | *.mdf
252 | *.ldf
253 | *.ndf
254 |
255 | # Business Intelligence projects
256 | *.rdl.data
257 | *.bim.layout
258 | *.bim_*.settings
259 | *.rptproj.rsuser
260 |
261 | # Microsoft Fakes
262 | FakesAssemblies/
263 |
264 | # GhostDoc plugin setting file
265 | *.GhostDoc.xml
266 |
267 | # Node.js Tools for Visual Studio
268 | .ntvs_analysis.dat
269 | node_modules/
270 |
271 | # Visual Studio 6 build log
272 | *.plg
273 |
274 | # Visual Studio 6 workspace options file
275 | *.opt
276 |
277 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
278 | *.vbw
279 |
280 | # Visual Studio LightSwitch build output
281 | **/*.HTMLClient/GeneratedArtifacts
282 | **/*.DesktopClient/GeneratedArtifacts
283 | **/*.DesktopClient/ModelManifest.xml
284 | **/*.Server/GeneratedArtifacts
285 | **/*.Server/ModelManifest.xml
286 | _Pvt_Extensions
287 |
288 | # Paket dependency manager
289 | .paket/paket.exe
290 | paket-files/
291 |
292 | # FAKE - F# Make
293 | .fake/
294 |
295 | # JetBrains Rider
296 | .idea/
297 | *.sln.iml
298 |
299 | # CodeRush personal settings
300 | .cr/personal
301 |
302 | # Python Tools for Visual Studio (PTVS)
303 | __pycache__/
304 | *.pyc
305 |
306 | # Cake - Uncomment if you are using it
307 | # tools/**
308 | # !tools/packages.config
309 |
310 | # Tabs Studio
311 | *.tss
312 |
313 | # Telerik's JustMock configuration file
314 | *.jmconfig
315 |
316 | # BizTalk build output
317 | *.btp.cs
318 | *.btm.cs
319 | *.odx.cs
320 | *.xsd.cs
321 |
322 | # OpenCover UI analysis results
323 | OpenCover/
324 |
325 | # Azure Stream Analytics local run output
326 | ASALocalRun/
327 |
328 | # MSBuild Binary and Structured Log
329 | *.binlog
330 |
331 | # NVidia Nsight GPU debugger configuration file
332 | *.nvuser
333 |
334 | # MFractors (Xamarin productivity tool) working folder
335 | .mfractor/
336 |
337 | # Local History for Visual Studio
338 | .localhistory/
339 |
340 | # BeatPulse healthcheck temp database
341 | healthchecksdb
342 |
--------------------------------------------------------------------------------
/Karonda.ModbusTcp.Client/Karonda.ModbusTcp.Client.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp2.1
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/Karonda.ModbusTcp.Client/Program.cs:
--------------------------------------------------------------------------------
1 | using DotNetty.Common.Internal.Logging;
2 | using Karonda.ModbusTcp.Entity;
3 | using Karonda.ModbusTcp.Entity.Function.Response;
4 | using Microsoft.Extensions.Logging.Console;
5 | using System;
6 | using System.Threading.Tasks;
7 |
8 | namespace Karonda.ModbusTcp.Client
9 | {
10 | class Program
11 | {
12 | static async Task RunClientAsync()
13 | {
14 | //InternalLoggerFactory.DefaultFactory.AddProvider(new ConsoleLoggerProvider((s, level) => true, false));
15 |
16 | ModbusClient client = new ModbusClient(0x01, "127.0.0.1");
17 |
18 | try
19 | {
20 | await client.Connect();
21 |
22 | while(true)
23 | {
24 | Console.WriteLine(@"
25 | <------------------------------------------------------->
26 | 1: Read Coils; 2: Read Discrete Inputs;
27 | 3: Read Holding Registers; 4: Read Input Registers;
28 | 5: Write Single Coil; 6: Write Single Register;
29 | 15: Write Multiple Coils; 16: Write Multiple Registers;
30 | <------------------------------------------------------->");
31 | var line = Console.ReadLine();
32 | if (string.IsNullOrEmpty(line)) break;
33 |
34 | Console.WriteLine("<------------------------------------------------------->");
35 | var command = Convert.ToInt32(line);
36 |
37 | ModbusFunction response = null;
38 | ushort startingAddress = 0x0000;
39 | ushort quantity = 0x000A;
40 | var state = true;
41 | ushort value = 0x0001;
42 |
43 | switch (command)
44 | {
45 | case 1:
46 | response = client.ReadCoils(startingAddress, quantity);
47 | var coils = (response as ReadCoilsResponse).Coils;
48 | for(int i =0;i< quantity; i++)
49 | {
50 | Console.WriteLine(coils[i]);
51 | }
52 | break;
53 | case 2:
54 | response = client.ReadDiscreteInputs(startingAddress, quantity);
55 | var inputs = (response as ReadDiscreteInputsResponse).Inputs;
56 | for (int i = 0; i < quantity; i++)
57 | {
58 | Console.WriteLine(inputs[i]);
59 | }
60 | break;
61 | case 3:
62 | response = client.ReadHoldingRegisters(startingAddress, quantity);
63 | foreach(var register in (response as ReadHoldingRegistersResponse).Registers)
64 | {
65 | Console.WriteLine(register);
66 | }
67 | break;
68 | case 4:
69 | response = client.ReadInputRegisters(startingAddress, quantity);
70 | foreach (var register in (response as ReadInputRegistersResponse).Registers)
71 | {
72 | Console.WriteLine(register);
73 | }
74 | break;
75 | case 5:
76 | response = client.WriteSingleCoil(startingAddress, state);
77 | Console.WriteLine((response as WriteSingleCoilResponse).State == state ? "Successed" : "Failed");
78 | break;
79 | case 6:
80 | response = client.WriteSingleRegister(startingAddress, value);
81 | Console.WriteLine((response as WriteSingleRegisterResponse).Value == value ? "Successed" : "Failed");
82 | break;
83 | case 15:
84 | var states = new bool[] { true, true, true, true, true, true, true, true, true, true };
85 | response = client.WriteMultipleCoils(startingAddress, states);
86 | Console.WriteLine((response as WriteMultipleCoilsResponse).Quantity == states.Length);
87 | break;
88 | case 16:
89 | var registers = new ushort[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
90 | response = client.WriteMultipleRegisters(startingAddress, registers);
91 | Console.WriteLine((response as WriteMultipleRegistersResponse).Quantity == registers.Length);
92 | break;
93 | }
94 |
95 | }
96 |
97 | await client.Close();
98 |
99 | Console.ReadLine();
100 | }
101 | catch (Exception exception)
102 | {
103 | Console.WriteLine(exception.Message);
104 | }
105 | }
106 |
107 | static void Main() => RunClientAsync().Wait();
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/Karonda.ModbusTcp.Server/Karonda.ModbusTcp.Server.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp2.1
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/Karonda.ModbusTcp.Server/ModbusResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Text;
5 | using Karonda.ModbusTcp.Entity;
6 | using Karonda.ModbusTcp.Entity.Function.Request;
7 | using Karonda.ModbusTcp.Entity.Function.Response;
8 |
9 | namespace Karonda.ModbusTcp.Server
10 | {
11 | public class ModbusResponse : ModbusResponseService
12 | {
13 | public override ModbusFunction ReadCoils(ReadCoilsRequest request)
14 | {
15 | var coilArray = ReadCoilsOrInputs(request.Quantity);
16 | var response = new ReadCoilsResponse(coilArray);
17 |
18 | return response;
19 | }
20 |
21 | public override ModbusFunction ReadDiscreteInputs(ReadDiscreteInputsRequest request)
22 | {
23 | var inputArray = ReadCoilsOrInputs(request.Quantity);
24 | var response = new ReadDiscreteInputsResponse(inputArray);
25 |
26 | return response;
27 | }
28 |
29 | public override ModbusFunction ReadHoldingRegisters(ReadHoldingRegistersRequest request)
30 | {
31 | var registers = ReadRegisters(request.Quantity);
32 | var response = new ReadHoldingRegistersResponse(registers);
33 |
34 | return response;
35 | }
36 |
37 | public override ModbusFunction ReadInputRegisters(ReadInputRegistersRequest request)
38 | {
39 | var registers = ReadRegisters(request.Quantity);
40 | var response = new ReadInputRegistersResponse(registers);
41 |
42 | return response;
43 | }
44 |
45 | public override ModbusFunction WriteSingleCoil(WriteSingleCoilRequest request)
46 | {
47 | var response = new WriteSingleCoilResponse(request.StartingAddress, request.State);
48 | return response;
49 | }
50 |
51 | public override ModbusFunction WriteSingleRegister(WriteSingleRegisterRequest request)
52 | {
53 | var response = new WriteSingleRegisterResponse(request.StartingAddress, request.Value);
54 | return response;
55 | }
56 |
57 | public override ModbusFunction WriteMultipleCoils(WriteMultipleCoilsRequest request)
58 | {
59 | var response = new WriteMultipleCoilsResponse(request.StartingAddress, request.Quantity);
60 | return response;
61 | }
62 |
63 | public override ModbusFunction WriteMultipleRegisters(WriteMultipleRegistersRequest request)
64 | {
65 | var response = new WriteMultipleRegistersResponse(request.StartingAddress, request.Quantity);
66 | return response;
67 | }
68 |
69 | private BitArray ReadCoilsOrInputs(ushort quantity)
70 | {
71 | var length = quantity + (8 - quantity % 8) % 8;
72 | var coils = new bool[length];
73 |
74 | Random ran = new Random();
75 | // from low to high
76 | for (int i = 0; i < coils.Length; i++)
77 | {
78 | if (i < quantity)
79 | {
80 | coils[i] = ran.Next() % 2 == 0;
81 | //coils[i] = true;
82 | }
83 | else
84 | {
85 | coils[i] = false;
86 | }
87 | }
88 |
89 | var arr = new BitArray(coils);
90 | return arr;
91 | }
92 |
93 | private ushort[] ReadRegisters(ushort quantity)
94 | {
95 | var registers = new ushort[quantity];
96 |
97 | Random ran = new Random();
98 | for (int i = 0; i < registers.Length; i++)
99 | {
100 | registers[i] = (ushort)ran.Next(ushort.MinValue, ushort.MaxValue);
101 | }
102 |
103 | return registers;
104 | }
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/Karonda.ModbusTcp.Server/Program.cs:
--------------------------------------------------------------------------------
1 | using DotNetty.Common.Internal.Logging;
2 | using Microsoft.Extensions.Logging.Console;
3 | using System;
4 | using System.Threading.Tasks;
5 |
6 | namespace Karonda.ModbusTcp.Server
7 | {
8 | class Program
9 | {
10 | static async Task RunServerAsync()
11 | {
12 | //InternalLoggerFactory.DefaultFactory.AddProvider(new ConsoleLoggerProvider((s, level) => true, false));
13 |
14 | ModbusResponse response = new ModbusResponse();
15 | ModbusServer server = new ModbusServer(response);
16 |
17 | await server.Start();
18 |
19 | Console.WriteLine("Server Started");
20 | Console.ReadLine();
21 |
22 | await server.Stop();
23 | }
24 |
25 | static void Main() => RunServerAsync().Wait();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Karonda.ModbusTcp.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.28010.2050
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Karonda.ModbusTcp", "Karonda.ModbusTcp\Karonda.ModbusTcp.csproj", "{6D7055D9-D2E6-4D48-9AA1-440E844F34E1}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Karonda.ModbusTcp.Client", "Karonda.ModbusTcp.Client\Karonda.ModbusTcp.Client.csproj", "{262F1BBF-A902-4F8B-B367-62388502DDFF}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Karonda.ModbusTcp.Server", "Karonda.ModbusTcp.Server\Karonda.ModbusTcp.Server.csproj", "{57CC2EE2-F4DD-4475-947A-D54F9CB33B93}"
11 | EndProject
12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F445E325-BD11-485C-BC4D-A560C9BBA8FE}"
13 | EndProject
14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{FE0D1E90-583B-4612-8C3A-069CA15CDF69}"
15 | EndProject
16 | Global
17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
18 | Debug|Any CPU = Debug|Any CPU
19 | Release|Any CPU = Release|Any CPU
20 | EndGlobalSection
21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
22 | {6D7055D9-D2E6-4D48-9AA1-440E844F34E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {6D7055D9-D2E6-4D48-9AA1-440E844F34E1}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {6D7055D9-D2E6-4D48-9AA1-440E844F34E1}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {6D7055D9-D2E6-4D48-9AA1-440E844F34E1}.Release|Any CPU.Build.0 = Release|Any CPU
26 | {262F1BBF-A902-4F8B-B367-62388502DDFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {262F1BBF-A902-4F8B-B367-62388502DDFF}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {262F1BBF-A902-4F8B-B367-62388502DDFF}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {262F1BBF-A902-4F8B-B367-62388502DDFF}.Release|Any CPU.Build.0 = Release|Any CPU
30 | {57CC2EE2-F4DD-4475-947A-D54F9CB33B93}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31 | {57CC2EE2-F4DD-4475-947A-D54F9CB33B93}.Debug|Any CPU.Build.0 = Debug|Any CPU
32 | {57CC2EE2-F4DD-4475-947A-D54F9CB33B93}.Release|Any CPU.ActiveCfg = Release|Any CPU
33 | {57CC2EE2-F4DD-4475-947A-D54F9CB33B93}.Release|Any CPU.Build.0 = Release|Any CPU
34 | EndGlobalSection
35 | GlobalSection(SolutionProperties) = preSolution
36 | HideSolutionNode = FALSE
37 | EndGlobalSection
38 | GlobalSection(NestedProjects) = preSolution
39 | {6D7055D9-D2E6-4D48-9AA1-440E844F34E1} = {F445E325-BD11-485C-BC4D-A560C9BBA8FE}
40 | {262F1BBF-A902-4F8B-B367-62388502DDFF} = {FE0D1E90-583B-4612-8C3A-069CA15CDF69}
41 | {57CC2EE2-F4DD-4475-947A-D54F9CB33B93} = {FE0D1E90-583B-4612-8C3A-069CA15CDF69}
42 | EndGlobalSection
43 | GlobalSection(ExtensibilityGlobals) = postSolution
44 | SolutionGuid = {87424D19-0664-43CE-B730-54D88491D865}
45 | EndGlobalSection
46 | EndGlobal
47 |
--------------------------------------------------------------------------------
/Karonda.ModbusTcp/Entity/Function/ExceptionFunction.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using DotNetty.Buffers;
5 |
6 | namespace Karonda.ModbusTcp.Entity.Function
7 | {
8 | /*
9 | * Modbus Exception Codes
10 | *
11 | * 01 ILLEGAL FUNCTION
12 | *
13 | * The function code received in the query is not an allowable action for
14 | * the server (or slave). This may be because the function code is only
15 | * applicable to newer devices, and was not implemented in the unit
16 | * selected. It could also indicate that the server (or slave) is in the
17 | * wrong state to process a request of this type, for example because it is
18 | * unconfigured and is being asked to return register values.
19 | *
20 | * 02 ILLEGAL DATA ADDRESS
21 | *
22 | * The data address received in the query is not an allowable address for
23 | * the server (or slave). More specifically, the combination of reference
24 | * number and transfer length is invalid. For a controller with 100
25 | * registers, the PDU addresses the first register as 0, and the last one as
26 | * 99. If a request is submitted with a starting register address of 96 and
27 | * a quantity of registers of 4, then this request will successfully operate
28 | * (address-wise at least) on registers 96, 97, 98, 99. If a request is
29 | * submitted with a starting register address of 96 and a quantity of
30 | * registers of 5, then this request will fail with Exception Code 0x02
31 | * “Illegal Data Address” since it attempts to operate on registers 96, 97,
32 | * 98, 99 and 100, and there is no register with address 100.
33 | *
34 | * 03 ILLEGAL DATA VALUE
35 | *
36 | * A value contained in the query data field is not an allowable value for
37 | * server (or slave). This indicates a fault in the structure of the
38 | * remainder of a complex request, such as that the implied length is
39 | * incorrect. It specifically does NOT mean that a data item submitted for
40 | * storage in a register has a value outside the expectation of the
41 | * application program, since the MODBUS protocol is unaware of the
42 | * significance of any particular value of any particular register.
43 | *
44 | * 04 SLAVE DEVICE FAILURE
45 | *
46 | * An unrecoverable error occurred while the server (or slave) was
47 | * attempting to perform the requested action.
48 | *
49 | * 05 ACKNOWLEDGE
50 | *
51 | * Specialized use in conjunction with programming commands. The server (or
52 | * slave) has accepted the request and is processing it, but a long duration
53 | * of time will be required to do so. This response is returned to prevent a
54 | * timeout error from occurring in the client (or master). The client (or
55 | * master) can next issue a Poll Program Complete message to determine if
56 | * processing is completed.
57 | *
58 | * 06 SLAVE DEVICE BUSY
59 | *
60 | * Specialized use in conjunction with programming commands. The server (or
61 | * slave) is engaged in processing a long–duration program command. The
62 | * client (or master) should retransmit the message later when the server
63 | * (or slave) is free.
64 | *
65 | * 08 MEMORY PARITY ERROR
66 | *
67 | * Specialized use in conjunction with function codes 20 and 21 and
68 | * reference type 6, to indicate that the extended file area failed to pass
69 | * a consistency check. The server (or slave) attempted to read record file,
70 | * but detected a parity error in the memory. The client (or master) can
71 | * retry the request, but service may be required on the server (or slave)
72 | * device.
73 | *
74 | * 0A GATEWAY PATH UNAVAILABLE
75 | *
76 | * Specialized use in conjunction with gateways, indicates that the gateway
77 | * was unable to allocate an internal communication path from the input port
78 | * to the output port for processing the request. Usually means that the
79 | * gateway is misconfigured or overloaded.
80 | *
81 | * 0B GATEWAY TARGET DEVICE FAILED TO RESPOND
82 | *
83 | * Specialized use in conjunction with gateways, indicates that no response
84 | * was obtained from the target device. Usually means that the device is not
85 | * present on the network.
86 | */
87 | public class ExceptionFunction : ModbusFunction
88 | {
89 | public short ExceptionCode { get; private set; }
90 | public string ExceptionMessage
91 | {
92 | get
93 | {
94 | if (exceptions.ContainsKey(ExceptionCode))
95 | {
96 | return exceptions[ExceptionCode];
97 | }
98 | return "Undefined Error";
99 | }
100 | }
101 | private static readonly Dictionary exceptions = new Dictionary
102 | {
103 | {0x01, "Illegal Function"},
104 | {0x02, "Illegal Data Address"},
105 | {0x03, "Illegal Data Value"},
106 | {0x04, "Slave Device Failure"},
107 | {0x05, "Acknowledge"},
108 | {0x06, "Slave Device Busy"},
109 | {0x08, "Memory Parity Error"},
110 | {0x0A, "Gateway Path Unavailable"},
111 | {0x0B, "Gateway Target Device Failed To Respond"},
112 |
113 | };
114 | public ExceptionFunction(short functionCode)
115 | : base(functionCode)
116 | {
117 | }
118 | public ExceptionFunction(short functionCode, short exceptionCode)
119 | : base(functionCode)
120 | {
121 | ExceptionCode = exceptionCode;
122 | }
123 | public override int CalculateLength()
124 | {
125 | return 1;
126 | }
127 |
128 | public override void Decode(IByteBuffer buffer)
129 | {
130 | ExceptionCode = buffer.ReadByte();
131 | }
132 |
133 | public override IByteBuffer Encode()
134 | {
135 | IByteBuffer buffer = Unpooled.Buffer();
136 | buffer.WriteByte(FunctionCode);
137 |
138 | buffer.WriteByte(ExceptionCode);
139 |
140 | return buffer;
141 | }
142 |
143 | public override string ToString()
144 | {
145 | return $"Exception Code: {ExceptionCode}, Message: {ExceptionMessage}";
146 | }
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/Karonda.ModbusTcp/Entity/Function/ReadCoilsInputsResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Text;
5 | using DotNetty.Buffers;
6 |
7 | namespace Karonda.ModbusTcp.Entity.Function
8 | {
9 | public abstract class ReadCoilsInputsResponse : ModbusFunction
10 | {
11 | private ushort byteCount;
12 | protected BitArray CoilsOrInputs { get; private set; }
13 | public ReadCoilsInputsResponse(short functionCode)
14 | : base(functionCode)
15 | {
16 |
17 | }
18 |
19 | public ReadCoilsInputsResponse(short functionCode, BitArray coilsOrInputs)
20 | : this(functionCode)
21 | {
22 | CoilsOrInputs = coilsOrInputs;
23 | byteCount = (ushort)(CoilsOrInputs.Length / 8);
24 | }
25 |
26 | public override int CalculateLength()
27 | {
28 | return 1 + byteCount;
29 | }
30 |
31 | public override void Decode(IByteBuffer buffer)
32 | {
33 | byteCount = buffer.ReadByte();
34 | var coilsOrInputs = new Byte[byteCount];
35 | buffer.ReadBytes(coilsOrInputs);
36 |
37 | CoilsOrInputs = new BitArray(coilsOrInputs);
38 | }
39 |
40 | public override IByteBuffer Encode()
41 | {
42 | var coilsOrInputs = new Byte[byteCount];
43 | CoilsOrInputs.CopyTo(coilsOrInputs, 0);
44 |
45 | IByteBuffer buffer = Unpooled.Buffer();
46 | buffer.WriteByte(FunctionCode);
47 | buffer.WriteByte(byteCount);
48 |
49 | buffer.WriteBytes(coilsOrInputs);
50 |
51 | return buffer;
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Karonda.ModbusTcp/Entity/Function/ReadRegistersResponse.cs:
--------------------------------------------------------------------------------
1 | using DotNetty.Buffers;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Text;
5 |
6 | namespace Karonda.ModbusTcp.Entity.Function
7 | {
8 | public abstract class ReadRegistersResponse : ModbusFunction
9 | {
10 | private ushort byteCount;
11 | public ushort[] Registers { get; private set; }
12 | public ReadRegistersResponse(short functionCode)
13 | : base(functionCode)
14 | {
15 |
16 | }
17 |
18 | public ReadRegistersResponse(short functionCode, ushort[] registers)
19 | : this(functionCode)
20 | {
21 | Registers = registers;
22 | byteCount = (ushort)(registers.Length * 2);
23 | }
24 |
25 | public override int CalculateLength()
26 | {
27 | return 1 + byteCount;
28 | }
29 |
30 | public override void Decode(IByteBuffer buffer)
31 | {
32 | byteCount = buffer.ReadByte();
33 | Registers = new ushort[byteCount / 2];
34 | for (int i = 0; i < Registers.Length; i++)
35 | {
36 | Registers[i] = buffer.ReadUnsignedShort();
37 | }
38 |
39 | }
40 |
41 | public override IByteBuffer Encode()
42 | {
43 | IByteBuffer buffer = Unpooled.Buffer();
44 | buffer.WriteByte(FunctionCode);
45 | buffer.WriteByte(byteCount);
46 |
47 | foreach (var register in Registers)
48 | {
49 | buffer.WriteUnsignedShort(register);
50 | }
51 |
52 | return buffer;
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Karonda.ModbusTcp/Entity/Function/ReadWriteMultiple.cs:
--------------------------------------------------------------------------------
1 | using DotNetty.Buffers;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Text;
5 |
6 | namespace Karonda.ModbusTcp.Entity.Function
7 | {
8 | public abstract class ReadWriteMultiple : ModbusFunction
9 | {
10 | private readonly ushort MaxReadCoilsQuantity = 0x07D0;// 2000
11 | private readonly ushort MaxWriteCoilsQuantity = 0x07B0;// 1968
12 | private readonly ushort MaxReadRegistersQuantity = 0x007D;// 125
13 | private readonly ushort MaxWriteRegistersQuantity = 0x007B;//123
14 | private readonly string QuantityOutOfRange = "quantities must no more than {0}";
15 |
16 | public ushort StartingAddress { get; private set; }
17 | public ushort Quantity { get; private set; }
18 |
19 | public ReadWriteMultiple(short functionCode)
20 | : base(functionCode)
21 | {
22 | }
23 |
24 | public ReadWriteMultiple(short functionCode, ushort startingAddress, ushort quantity)
25 | : base(functionCode)
26 | {
27 | StartingAddress = startingAddress;
28 | Quantity = quantity;
29 |
30 | WhetherQuantityIsOutOfRange();
31 | }
32 |
33 | public override int CalculateLength()
34 | {
35 | return 2 + 2; // StartingAddress Length + Quantity Length
36 | }
37 | public override void Decode(IByteBuffer buffer)
38 | {
39 | StartingAddress = buffer.ReadUnsignedShort();
40 | Quantity = buffer.ReadUnsignedShort();
41 |
42 | WhetherQuantityIsOutOfRange();
43 | }
44 | public override IByteBuffer Encode()
45 | {
46 | IByteBuffer buffer = Unpooled.Buffer();
47 | buffer.WriteByte(FunctionCode);
48 |
49 | buffer.WriteUnsignedShort(StartingAddress);
50 | buffer.WriteUnsignedShort(Quantity);
51 |
52 | return buffer;
53 | }
54 |
55 | private void WhetherQuantityIsOutOfRange()
56 | {
57 | var exceptionMessage = string.Empty;
58 | switch ((ModbusCommand)FunctionCode)
59 | {
60 | case ModbusCommand.ReadCoils:
61 | case ModbusCommand.ReadDiscreteInputs:
62 | if (Quantity > MaxReadCoilsQuantity)
63 | exceptionMessage = string.Format(QuantityOutOfRange, MaxReadCoilsQuantity);
64 | break;
65 | case ModbusCommand.ReadHoldingRegisters:
66 | case ModbusCommand.ReadInputRegisters:
67 | if (Quantity > MaxReadRegistersQuantity)
68 | exceptionMessage = string.Format(QuantityOutOfRange, MaxReadRegistersQuantity);
69 | break;
70 | case ModbusCommand.WriteMultipleCoils:
71 | if (Quantity > MaxWriteCoilsQuantity)
72 | exceptionMessage = string.Format(QuantityOutOfRange, MaxWriteCoilsQuantity);
73 | break;
74 | case ModbusCommand.WriteMultipleRegisters:
75 | if (Quantity > MaxWriteRegistersQuantity)
76 | exceptionMessage = string.Format(QuantityOutOfRange, MaxWriteRegistersQuantity);
77 | break;
78 | }
79 |
80 | if (!string.IsNullOrEmpty(exceptionMessage))
81 | throw new ArgumentOutOfRangeException(exceptionMessage);
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/Karonda.ModbusTcp/Entity/Function/Request/ReadCoilsRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Karonda.ModbusTcp.Entity.Function.Request
6 | {
7 | public class ReadCoilsRequest : ReadWriteMultiple
8 | {
9 | public ReadCoilsRequest()
10 | : base((short)ModbusCommand.ReadCoils)
11 | {
12 |
13 | }
14 |
15 | public ReadCoilsRequest(ushort startingAddress, ushort quantity)
16 | : base((short)ModbusCommand.ReadCoils, startingAddress, quantity)
17 | {
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Karonda.ModbusTcp/Entity/Function/Request/ReadDiscreteInputsRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Karonda.ModbusTcp.Entity.Function.Request
6 | {
7 | public class ReadDiscreteInputsRequest : ReadWriteMultiple
8 | {
9 | public ReadDiscreteInputsRequest()
10 | : base((short)ModbusCommand.ReadDiscreteInputs)
11 | {
12 |
13 | }
14 |
15 | public ReadDiscreteInputsRequest(ushort startingAddress, ushort quantity)
16 | : base((short)ModbusCommand.ReadDiscreteInputs, startingAddress, quantity)
17 | {
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Karonda.ModbusTcp/Entity/Function/Request/ReadHoldingRegistersRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Karonda.ModbusTcp.Entity.Function.Request
6 | {
7 | public class ReadHoldingRegistersRequest : ReadWriteMultiple
8 | {
9 | public ReadHoldingRegistersRequest()
10 | : base((short)ModbusCommand.ReadHoldingRegisters)
11 | {
12 |
13 | }
14 |
15 | public ReadHoldingRegistersRequest(ushort startingAddress, ushort quantity)
16 | : base((short)ModbusCommand.ReadHoldingRegisters, startingAddress, quantity)
17 | {
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Karonda.ModbusTcp/Entity/Function/Request/ReadInputRegistersRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Karonda.ModbusTcp.Entity.Function.Request
6 | {
7 | public class ReadInputRegistersRequest : ReadWriteMultiple
8 | {
9 | public ReadInputRegistersRequest()
10 | : base((short)ModbusCommand.ReadInputRegisters)
11 | {
12 |
13 | }
14 |
15 | public ReadInputRegistersRequest(ushort startingAddress, ushort quantity)
16 | : base((short)ModbusCommand.ReadInputRegisters, startingAddress, quantity)
17 | {
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Karonda.ModbusTcp/Entity/Function/Request/WriteMultipleCoilsRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Text;
5 | using DotNetty.Buffers;
6 |
7 | namespace Karonda.ModbusTcp.Entity.Function.Request
8 | {
9 | public class WriteMultipleCoilsRequest : ReadWriteMultiple
10 | {
11 | private ushort byteCount;
12 | public BitArray Coils { get; private set; }
13 | public WriteMultipleCoilsRequest()
14 | : base((short)ModbusCommand.WriteMultipleCoils)
15 | {
16 |
17 | }
18 |
19 | public WriteMultipleCoilsRequest(ushort startingAddress, bool[] states)
20 | : base((short)ModbusCommand.WriteMultipleCoils, startingAddress, (ushort)states.Length)
21 | {
22 | var length = Quantity + (8 - Quantity % 8) % 8;
23 |
24 | if(length > Quantity)
25 | {
26 | var finalCoils = new List();
27 | finalCoils.AddRange(states);
28 | for(var i = Quantity; i < length; i++)
29 | {
30 | finalCoils.Add(false);
31 | }
32 |
33 | Coils = new BitArray(finalCoils.ToArray());
34 | }
35 | else
36 | {
37 | Coils = new BitArray(states);
38 | }
39 |
40 | byteCount = (ushort)(Coils.Length / 8);
41 | }
42 |
43 | public override int CalculateLength()
44 | {
45 | return base.CalculateLength() + 1 + byteCount;
46 | }
47 |
48 | public override void Decode(IByteBuffer buffer)
49 | {
50 | base.Decode(buffer);
51 |
52 | byteCount = buffer.ReadByte();
53 | var coils = new byte[byteCount];
54 | buffer.ReadBytes(coils);
55 |
56 | Coils = new BitArray(coils);
57 | }
58 |
59 | public override IByteBuffer Encode()
60 | {
61 | var coils = new byte[byteCount];
62 | Coils.CopyTo(coils, 0);
63 |
64 | IByteBuffer buffer = base.Encode();
65 |
66 | buffer.WriteByte(byteCount);
67 |
68 | buffer.WriteBytes(coils);
69 |
70 | return buffer;
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/Karonda.ModbusTcp/Entity/Function/Request/WriteMultipleRegistersRequest.cs:
--------------------------------------------------------------------------------
1 | using DotNetty.Buffers;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Text;
5 |
6 | namespace Karonda.ModbusTcp.Entity.Function.Request
7 | {
8 | public class WriteMultipleRegistersRequest : ReadWriteMultiple
9 | {
10 | private ushort byteCount;
11 | public ushort[] Registers { get; private set; }
12 | public WriteMultipleRegistersRequest()
13 | : base((short)ModbusCommand.WriteMultipleRegisters)
14 | {
15 |
16 | }
17 |
18 | public WriteMultipleRegistersRequest(ushort startingAddress, ushort[] registers)
19 | : base((short)ModbusCommand.WriteMultipleRegisters, startingAddress, (ushort)registers.Length)
20 | {
21 | byteCount = (ushort)(Quantity * 2);
22 | Registers = registers;
23 | }
24 |
25 | public override int CalculateLength()
26 | {
27 | return base.CalculateLength() + 1 + byteCount;
28 | }
29 |
30 | public override void Decode(IByteBuffer buffer)
31 | {
32 | base.Decode(buffer);
33 |
34 | byteCount = buffer.ReadByte();
35 |
36 | Registers = new ushort[byteCount / 2];
37 | for(int i=0;i
12 | * <---------------------------- MBAP -----------------------------------------><------------- PDU ------------->
13 | * +------------------------+---------------------+----------+-----------------++---------------+---------------+
14 | * | Transaction Identifier | Protocol Identifier | Length | Unit Identifier || Function Code | Data |
15 | * | (2 Byte) | (2 Byte) | (2 Byte) | (1 Byte) || (1 Byte) | (1 - 252 Byte |
16 | * +------------------------+---------------------+----------+-----------------++---------------+---------------+
17 | */
18 |
19 | namespace Karonda.ModbusTcp.Entity
20 | {
21 | public class ModbusFrame
22 | {
23 | public ModbusHeader Header { get; set; }
24 | public ModbusFunction Function { get; set; }
25 |
26 | public ModbusFrame(ModbusHeader header, ModbusFunction function)
27 | {
28 | Header = header;
29 | Function = function;
30 | }
31 |
32 | public IByteBuffer Encode()
33 | {
34 | Header.Length = (ushort)(1 + 1 + Function.CalculateLength());// Unit Identifier + Function Code + data length
35 |
36 | IByteBuffer buffer = Unpooled.Buffer();
37 |
38 | buffer.WriteBytes(Header.Encode());
39 | buffer.WriteBytes(Function.Encode());
40 |
41 | return buffer;
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Karonda.ModbusTcp/Entity/ModbusFunction.cs:
--------------------------------------------------------------------------------
1 | using DotNetty.Buffers;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Text;
5 |
6 | namespace Karonda.ModbusTcp.Entity
7 | {
8 | public abstract class ModbusFunction
9 | {
10 | protected short FunctionCode { get; }
11 |
12 | protected ModbusFunction(short functionCode)
13 | {
14 | FunctionCode = functionCode;
15 | }
16 | ///
17 | /// PDU length -1 (not include function code length)
18 | ///
19 | ///
20 | public abstract int CalculateLength();
21 |
22 | public abstract void Decode(IByteBuffer buffer);
23 |
24 | public abstract IByteBuffer Encode();
25 |
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Karonda.ModbusTcp/Entity/ModbusHeader.cs:
--------------------------------------------------------------------------------
1 | using DotNetty.Buffers;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Text;
5 |
6 | namespace Karonda.ModbusTcp.Entity
7 | {
8 | public class ModbusHeader
9 | {
10 | public ushort TransactionIdentifier { get; set; }
11 | public ushort ProtocolIdentifier { get; set; }
12 | public ushort Length { get; set; }
13 | public short UnitIdentifier { get; set; }
14 |
15 | public ModbusHeader(IByteBuffer buffer)
16 | {
17 | TransactionIdentifier = buffer.ReadUnsignedShort();
18 | ProtocolIdentifier = buffer.ReadUnsignedShort();
19 | Length = buffer.ReadUnsignedShort();
20 | UnitIdentifier = buffer.ReadByte(); // readUnsignedByte
21 | }
22 |
23 | public ModbusHeader(ushort transactionIdentifier, short unitIdentifier)
24 | : this(transactionIdentifier, 0x0000, unitIdentifier) // for modbus protocol: Protocol Identifier = 0x00
25 | {
26 |
27 | }
28 |
29 | private ModbusHeader(ushort transactionIdentifier, ushort protocolIdentifier, short unitIdentifier)
30 | {
31 | TransactionIdentifier = transactionIdentifier;
32 | ProtocolIdentifier = protocolIdentifier;
33 | UnitIdentifier = unitIdentifier;
34 | }
35 |
36 | public IByteBuffer Encode()
37 | {
38 | IByteBuffer buffer = Unpooled.Buffer();
39 |
40 | buffer.WriteUnsignedShort(TransactionIdentifier);
41 | buffer.WriteUnsignedShort(ProtocolIdentifier);
42 | buffer.WriteUnsignedShort(Length);
43 | buffer.WriteByte(UnitIdentifier);
44 |
45 | return buffer;
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Karonda.ModbusTcp/Handler/ModbusDecoder.cs:
--------------------------------------------------------------------------------
1 | using DotNetty.Buffers;
2 | using DotNetty.Codecs;
3 | using DotNetty.Transport.Channels;
4 | using Karonda.ModbusTcp.Entity;
5 | using Karonda.ModbusTcp.Entity.Function;
6 | using Karonda.ModbusTcp.Entity.Function.Request;
7 | using Karonda.ModbusTcp.Entity.Function.Response;
8 | using System;
9 | using System.Collections.Generic;
10 | using System.Text;
11 |
12 | namespace Karonda.ModbusTcp.Handler
13 | {
14 | public class ModbusDecoder : ByteToMessageDecoder
15 | {
16 | private bool isServerMode;
17 | private readonly short maxFunctionCode = 0x80;
18 | private readonly string typeName = "Karonda.ModbusTcp.Entity.Function.{0}.{1}{0}";
19 |
20 | public ModbusDecoder(bool isServerMode)
21 | {
22 | this.isServerMode = isServerMode;
23 | }
24 | protected override void Decode(IChannelHandlerContext context, IByteBuffer input, List