Compare commits

..

31 Commits

Author SHA1 Message Date
parallelbgls
e7cad88bcf HJ212 Length Limit 2025-10-30 17:19:54 +08:00
parallelbgls
7f304e826f Add AddressCombinerStatic 2025-10-20 15:08:07 +08:00
parallelbgls
51ded333d9 Fix 2025-05-21 12:04:12 +08:00
parallelbgls
62e6fcbfb1 Fix 2025-01-06 13:43:59 +08:00
parallelbgls
0f757d6996 Fix 2024-12-25 09:10:22 +08:00
parallelbgls
80d6e843af Fix 2024-12-23 16:56:18 +08:00
parallelbgls
40b2b39d2b Fix 2024-12-23 16:22:52 +08:00
parallelbgls
c3c4125a4b Fix 2024-09-04 11:43:27 +08:00
parallelbgls
b4c57a42d0 Add alias to machine 2024-09-03 16:05:01 +08:00
parallelbgls
21c8e34934 Fix 2024-07-26 17:34:07 +08:00
parallelbgls
e38c16e899 Receiver Fix 2024-06-22 14:36:36 +08:00
parallelbgls
9cffd005a2 Add Modbus RTU receiver module 2024-06-19 13:55:18 +08:00
luosheng
a6b467cc96 Merge branch 'dev' of https://git.parallelbgls.top/luosheng/Modbus.Net into dev 2024-05-20 16:30:47 +08:00
luosheng
d0707a867a hj212 2024-05-20 16:25:21 +08:00
luosheng
cc44c64dc4 Fix 2023-12-28 13:27:03 +08:00
luosheng
4ecec7a35e Com Window Temp Fix 2023-12-19 21:55:04 +08:00
luosheng
34ba703696 Server sample fix 2023-12-14 15:31:58 +08:00
luosheng
258da627ac Fix 2023-12-12 10:17:58 +08:00
luosheng
c6b5d9b928 3412 Alter 2023-12-12 09:05:57 +08:00
luosheng
0d65afd74f Fix 2023-12-07 16:33:19 +08:00
luosheng
986bb9b561 Fix 2023-12-07 16:04:54 +08:00
luosheng
d2594a3ff9 BigEndian3412 Enhancement 2023-12-02 11:59:23 +08:00
luosheng
f7507428b8 Remove OPC, Fix 0X and 1X read/write in Modbus 2023-12-02 07:55:13 +08:00
luosheng
9892eda959 Fix 2023-11-14 10:54:34 +08:00
luosheng
e9a8705b03 Fix 2023-11-14 10:36:25 +08:00
luosheng
b2bc6c82a9 Update 2023-10-26 10:27:57 +08:00
luosheng
d619bf36a1 Update 2023-10-16 05:40:32 +08:00
parallelbgls
d23b942464 Add receiver (not complete) 2023-10-12 15:16:48 +08:00
luosheng
511434d18a Fix 2023-09-08 09:45:16 +08:00
luosheng
fac39b0bf1 NoResponseController 2023-09-07 09:08:26 +08:00
Chris L.(Luo Sheng)
3acd6aa1e0 Merge pull request #24 from parallelbgls/master
Merge
2023-07-20 09:50:26 +08:00
309 changed files with 3155 additions and 58975 deletions

318
.gitignore vendored
View File

@@ -1,34 +1,84 @@
## Ignore Visual Studio temporary files, build results, and ## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons. ## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
# User-specific files # User-specific files
*.rsuser
*.suo *.suo
*.user *.user
*.userosscache
*.sln.docstates *.sln.docstates
# Build results # User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/ [Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/ [Rr]elease/
[Rr]eleases/
x64/ x64/
build/ x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/ [Bb]in/
[Oo]bj/ [Oo]bj/
[Ll]og/
[Ll]ogs/
# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets # Visual Studio 2015/2017 cache/options directory
!packages/*/build/ .vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results # MSTest test Results
[Tt]est[Rr]esult*/ [Tt]est[Rr]esult*/
[Bb]uild[Ll]og.* [Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c *_i.c
*_p.c *_p.c
*_h.h
*.ilk *.ilk
*.meta *.meta
*.obj *.obj
*.iobj
*.pch *.pch
*.pdb *.pdb
*.ipdb
*.pgc *.pgc
*.pgd *.pgd
*.rsp *.rsp
@@ -38,26 +88,41 @@ build/
*.tlh *.tlh
*.tmp *.tmp
*.tmp_proj *.tmp_proj
*_wpftmp.csproj
*.log *.log
*.tlog
*.vspscc *.vspscc
*.vssscc *.vssscc
.builds .builds
*.pidb *.pidb
*.log *.svclog
*.scc *.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files # Visual C++ cache files
ipch/ ipch/
*.aps *.aps
*.ncb *.ncb
*.opendb
*.opensdf *.opensdf
*.sdf *.sdf
*.cachefile *.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler # Visual Studio profiler
*.psess *.psess
*.vsp *.vsp
*.vspx *.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit # Guidance Automation Toolkit
*.gpState *.gpState
@@ -65,6 +130,7 @@ ipch/
# ReSharper is a .NET coding add-in # ReSharper is a .NET coding add-in
_ReSharper*/ _ReSharper*/
*.[Rr]e[Ss]harper *.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in # TeamCity is a build add-in
_TeamCity* _TeamCity*
@@ -72,9 +138,30 @@ _TeamCity*
# DotCover is a Code Coverage Tool # DotCover is a Code Coverage Tool
*.dotCover *.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch # NCrunch
*.ncrunch* _NCrunch_*
.*crunch*.local.xml .*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder # Installshield output folder
[Ee]xpress/ [Ee]xpress/
@@ -93,66 +180,219 @@ DocProject/Help/html
publish/ publish/
# Publish Web Output # Publish Web Output
*.Publish.xml *.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# NuGet Packages Directory # Microsoft Azure Web App publish settings. Comment the next line if you want to
## TODO: If you have NuGet Package Restore enabled, uncomment the next line # checkin your Azure Web App publish settings, but sensitive information contained
#packages/ # in these scripts will be unencrypted
PublishScripts/
# Windows Azure Build Output # NuGet Packages
csx *.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef *.build.csdef
# Windows Store app package directory # Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/ AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others # Others
sql/
*.Cache
ClientBin/ ClientBin/
[Ss]tyle[Cc]op.*
~$* ~$*
*~ *~
*.dbmdl *.dbmdl
*.[Pp]ublish.xml *.dbproj.schemaview
*.jfm
*.pfx *.pfx
*.publishsettings *.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects # RIA/Silverlight projects
Generated_Code/ Generated_Code/
# Backup & report files from converting an old project file to a newer # Backup & report files from converting an old project file
# Visual Studio version. Backup files are not needed, because we have git ;-) # to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/ _UpgradeReport_Files/
Backup*/ Backup*/
UpgradeLog*.XML UpgradeLog*.XML
UpgradeLog*.htm UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files # SQL Server files
App_Data/*.mdf *.mdf
App_Data/*.ldf *.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
#LightSwitch generated files # Microsoft Fakes
GeneratedArtifacts/ FakesAssemblies/
_Pvt_Extensions/
ModelManifest.xml
# ========================= # GhostDoc plugin setting file
# Windows detritus *.GhostDoc.xml
# =========================
# Windows image file caches # Node.js Tools for Visual Studio
Thumbs.db .ntvs_analysis.dat
ehthumbs.db node_modules/
# Folder config file # Visual Studio 6 build log
Desktop.ini *.plg
# Recycle Bin used on file shares # Visual Studio 6 workspace options file
$RECYCLE.BIN/ *.opt
# Mac desktop service store files # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
.DS_Store *.vbw
/Modbus.Net/packages
/Modbus.Net/.vs # Visual Studio 6 auto-generated project file (contains which files were open etc.)
*.vbp
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
*.ncb
*.aps
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# Visual Studio History (VSHistory) files
.vshistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp
# JetBrains Rider
*.sln.iml

View File

@@ -5,6 +5,8 @@ namespace Modbus.Net
public partial class Endian public partial class Endian
{ {
public const int BigEndian3412 = 10; public const int BigEndian3412 = 10;
public const int LittleEndian3412 = 11;
} }
public class BigEndian3412ValueHelper : BigEndianLsbValueHelper public class BigEndian3412ValueHelper : BigEndianLsbValueHelper
@@ -26,9 +28,9 @@ namespace Modbus.Net
/// <summary> /// <summary>
/// 是否为大端 /// 是否为大端
/// </summary> /// </summary>
protected new bool LittleEndian => false; public override bool LittleEndian => true;
protected new bool LittleEndianBit => false; public override bool LittleEndianBit => false;
/// <summary> /// <summary>
/// 覆盖的获取实例的方法 /// 覆盖的获取实例的方法
@@ -36,6 +38,70 @@ namespace Modbus.Net
public new static BigEndian3412ValueHelper Instance public new static BigEndian3412ValueHelper Instance
=> _bigEndian3412Instance ?? (_bigEndian3412Instance = new BigEndian3412ValueHelper()); => _bigEndian3412Instance ?? (_bigEndian3412Instance = new BigEndian3412ValueHelper());
public override int GetInt(byte[] data, ref int pos)
{
Array.Reverse(data, pos, 4);
byte temp;
temp = data[pos]; data[pos] = data[pos + 2]; data[pos + 2] = temp;
temp = data[pos + 1]; data[pos + 1] = data[pos + 3]; data[pos + 3] = temp;
var t = BitConverter.ToInt32(data, pos);
temp = data[pos]; data[pos] = data[pos + 2]; data[pos + 2] = temp;
temp = data[pos + 1]; data[pos + 1] = data[pos + 3]; data[pos + 3] = temp;
Array.Reverse(data, pos, 4);
pos += 4;
return t;
}
public override uint GetUInt(byte[] data, ref int pos)
{
Array.Reverse(data, pos, 4);
byte temp;
temp = data[pos]; data[pos] = data[pos + 2]; data[pos + 2] = temp;
temp = data[pos + 1]; data[pos + 1] = data[pos + 3]; data[pos + 3] = temp;
var t = BitConverter.ToUInt32(data, pos);
temp = data[pos]; data[pos] = data[pos + 2]; data[pos + 2] = temp;
temp = data[pos + 1]; data[pos + 1] = data[pos + 3]; data[pos + 3] = temp;
Array.Reverse(data, pos, 4);
pos += 4;
return t;
}
public override long GetLong(byte[] data, ref int pos)
{
Array.Reverse(data, pos, 8);
byte temp;
temp = data[pos]; data[pos] = data[pos + 6]; data[pos + 6] = temp;
temp = data[pos + 1]; data[pos + 1] = data[pos + 7]; data[pos + 7] = temp;
temp = data[pos + 2]; data[pos + 2] = data[pos + 4]; data[pos + 4] = temp;
temp = data[pos + 3]; data[pos + 3] = data[pos + 5]; data[pos + 5] = temp;
var t = BitConverter.ToInt64(data, pos);
temp = data[pos]; data[pos] = data[pos + 6]; data[pos + 6] = temp;
temp = data[pos + 1]; data[pos + 1] = data[pos + 7]; data[pos + 7] = temp;
temp = data[pos + 2]; data[pos + 2] = data[pos + 4]; data[pos + 4] = temp;
temp = data[pos + 3]; data[pos + 3] = data[pos + 5]; data[pos + 5] = temp;
Array.Reverse(data, pos, 8);
pos += 8;
return t;
}
public override ulong GetULong(byte[] data, ref int pos)
{
Array.Reverse(data, pos, 8);
byte temp;
temp = data[pos]; data[pos] = data[pos + 6]; data[pos + 6] = temp;
temp = data[pos + 1]; data[pos + 1] = data[pos + 7]; data[pos + 7] = temp;
temp = data[pos + 2]; data[pos + 2] = data[pos + 4]; data[pos + 4] = temp;
temp = data[pos + 3]; data[pos + 3] = data[pos + 5]; data[pos + 5] = temp;
var t = BitConverter.ToUInt64(data, pos);
temp = data[pos]; data[pos] = data[pos + 6]; data[pos + 6] = temp;
temp = data[pos + 1]; data[pos + 1] = data[pos + 7]; data[pos + 7] = temp;
temp = data[pos + 2]; data[pos + 2] = data[pos + 4]; data[pos + 4] = temp;
temp = data[pos + 3]; data[pos + 3] = data[pos + 5]; data[pos + 5] = temp;
Array.Reverse(data, pos, 8);
pos += 8;
return t;
}
public override float GetFloat(byte[] data, ref int pos) public override float GetFloat(byte[] data, ref int pos)
{ {
Array.Reverse(data, pos, 4); Array.Reverse(data, pos, 4);
@@ -49,5 +115,137 @@ namespace Modbus.Net
pos += 4; pos += 4;
return t; return t;
} }
public override double GetDouble(byte[] data, ref int pos)
{
Array.Reverse(data, pos, 8);
byte temp;
temp = data[pos]; data[pos] = data[pos + 6]; data[pos + 6] = temp;
temp = data[pos + 1]; data[pos + 1] = data[pos + 7]; data[pos + 7] = temp;
temp = data[pos + 2]; data[pos + 2] = data[pos + 4]; data[pos + 4] = temp;
temp = data[pos + 3]; data[pos + 3] = data[pos + 5]; data[pos + 5] = temp;
var t = BitConverter.ToDouble(data, pos);
temp = data[pos]; data[pos] = data[pos + 6]; data[pos + 6] = temp;
temp = data[pos + 1]; data[pos + 1] = data[pos + 7]; data[pos + 7] = temp;
temp = data[pos + 2]; data[pos + 2] = data[pos + 4]; data[pos + 4] = temp;
temp = data[pos + 3]; data[pos + 3] = data[pos + 5]; data[pos + 5] = temp;
Array.Reverse(data, pos, 8);
pos += 8;
return t;
}
}
public class LittleEndian3412ValueHelper : LittleEndianLsbValueHelper
{
private static LittleEndian3412ValueHelper _littleEndian3412Instance;
/// <summary>
/// 构造器
/// </summary>
protected LittleEndian3412ValueHelper()
{
}
/// <summary>
/// 覆写的实例获取
/// </summary>
protected override ValueHelper _Instance => _littleEndian3412Instance;
/// <summary>
/// 是否为大端
/// </summary>
public override bool LittleEndian => true;
public override bool LittleEndianBit => true;
/// <summary>
/// 覆盖的获取实例的方法
/// </summary>
public new static LittleEndian3412ValueHelper Instance
=> _littleEndian3412Instance ?? (_littleEndian3412Instance = new LittleEndian3412ValueHelper());
public override int GetInt(byte[] data, ref int pos)
{
byte temp;
temp = data[pos]; data[pos] = data[pos + 2]; data[pos + 2] = temp;
temp = data[pos + 1]; data[pos + 1] = data[pos + 3]; data[pos + 3] = temp;
var t = BitConverter.ToInt32(data, pos);
temp = data[pos]; data[pos] = data[pos + 2]; data[pos + 2] = temp;
temp = data[pos + 1]; data[pos + 1] = data[pos + 3]; data[pos + 3] = temp;
pos += 4;
return t;
}
public override uint GetUInt(byte[] data, ref int pos)
{
byte temp;
temp = data[pos]; data[pos] = data[pos + 2]; data[pos + 2] = temp;
temp = data[pos + 1]; data[pos + 1] = data[pos + 3]; data[pos + 3] = temp;
var t = BitConverter.ToUInt32(data, pos);
temp = data[pos]; data[pos] = data[pos + 2]; data[pos + 2] = temp;
temp = data[pos + 1]; data[pos + 1] = data[pos + 3]; data[pos + 3] = temp;
pos += 4;
return t;
}
public override long GetLong(byte[] data, ref int pos)
{
byte temp;
temp = data[pos]; data[pos] = data[pos + 6]; data[pos + 6] = temp;
temp = data[pos + 1]; data[pos + 1] = data[pos + 7]; data[pos + 7] = temp;
temp = data[pos + 2]; data[pos + 2] = data[pos + 4]; data[pos + 4] = temp;
temp = data[pos + 3]; data[pos + 3] = data[pos + 5]; data[pos + 5] = temp;
var t = BitConverter.ToInt64(data, pos);
temp = data[pos]; data[pos] = data[pos + 6]; data[pos + 6] = temp;
temp = data[pos + 1]; data[pos + 1] = data[pos + 7]; data[pos + 7] = temp;
temp = data[pos + 2]; data[pos + 2] = data[pos + 4]; data[pos + 4] = temp;
temp = data[pos + 3]; data[pos + 3] = data[pos + 5]; data[pos + 5] = temp;
pos += 8;
return t;
}
public override ulong GetULong(byte[] data, ref int pos)
{
byte temp;
temp = data[pos]; data[pos] = data[pos + 6]; data[pos + 6] = temp;
temp = data[pos + 1]; data[pos + 1] = data[pos + 7]; data[pos + 7] = temp;
temp = data[pos + 2]; data[pos + 2] = data[pos + 4]; data[pos + 4] = temp;
temp = data[pos + 3]; data[pos + 3] = data[pos + 5]; data[pos + 5] = temp;
var t = BitConverter.ToUInt64(data, pos);
temp = data[pos]; data[pos] = data[pos + 6]; data[pos + 6] = temp;
temp = data[pos + 1]; data[pos + 1] = data[pos + 7]; data[pos + 7] = temp;
temp = data[pos + 2]; data[pos + 2] = data[pos + 4]; data[pos + 4] = temp;
temp = data[pos + 3]; data[pos + 3] = data[pos + 5]; data[pos + 5] = temp;
pos += 8;
return t;
}
public override float GetFloat(byte[] data, ref int pos)
{
byte temp;
temp = data[pos]; data[pos] = data[pos + 2]; data[pos + 2] = temp;
temp = data[pos + 1]; data[pos + 1] = data[pos + 3]; data[pos + 3] = temp;
var t = BitConverter.ToSingle(data, pos);
temp = data[pos]; data[pos] = data[pos + 2]; data[pos + 2] = temp;
temp = data[pos + 1]; data[pos + 1] = data[pos + 3]; data[pos + 3] = temp;
pos += 4;
return t;
}
public override double GetDouble(byte[] data, ref int pos)
{
byte temp;
temp = data[pos]; data[pos] = data[pos + 6]; data[pos + 6] = temp;
temp = data[pos + 1]; data[pos + 1] = data[pos + 7]; data[pos + 7] = temp;
temp = data[pos + 2]; data[pos + 2] = data[pos + 4]; data[pos + 4] = temp;
temp = data[pos + 3]; data[pos + 3] = data[pos + 5]; data[pos + 5] = temp;
var t = BitConverter.ToDouble(data, pos);
temp = data[pos]; data[pos] = data[pos + 6]; data[pos + 6] = temp;
temp = data[pos + 1]; data[pos + 1] = data[pos + 7]; data[pos + 7] = temp;
temp = data[pos + 2]; data[pos + 2] = data[pos + 4]; data[pos + 4] = temp;
temp = data[pos + 3]; data[pos + 3] = data[pos + 5]; data[pos + 5] = temp;
pos += 8;
return t;
}
} }
} }

View File

@@ -8,7 +8,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.6.0" /> <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.1.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -1,6 +1,6 @@
namespace Modbus.Net.HJ212 namespace Modbus.Net.HJ212
{ {
public class HJ212Controller : FifoController public class HJ212Controller : NoResponseController
{ {
public HJ212Controller(string ip, int port) : base(int.Parse(ConfigurationReader.GetValue("TCP:" + ip + ":" + port, "FetchSleepTime"))) public HJ212Controller(string ip, int port) : base(int.Parse(ConfigurationReader.GetValue("TCP:" + ip + ":" + port, "FetchSleepTime")))
{ {

View File

@@ -27,8 +27,8 @@ namespace Modbus.Net.HJ212
/// </summary> /// </summary>
/// <param name="id">设备的ID号</param> /// <param name="id">设备的ID号</param>
/// <param name="connectionString">连接地址</param> /// <param name="connectionString">连接地址</param>
public HJ212Machine(TKey id, string connectionString, string st, string cn, string pw, string mn) public HJ212Machine(TKey id, string alias, string connectionString, string st, string cn, string pw, string mn)
: base(id, null, true) : base(id, alias, null, true)
{ {
BaseUtility = new HJ212Utility(connectionString); BaseUtility = new HJ212Utility(connectionString);
ST = st; ST = st;
@@ -68,7 +68,7 @@ namespace Modbus.Net.HJ212
var sendValues = new List<Dictionary<string, string>>() { formatValues }; var sendValues = new List<Dictionary<string, string>>() { formatValues };
//写入数据 //写入数据
await await
BaseUtility.GetUtilityMethods<IUtilityMethodDatas>().SetDatasAsync("0", new object[] { ST, CN, PW, MN, sendValues, DateTime.Now }); BaseUtility.GetUtilityMethods<IUtilityMethodDatas>().SetDatasAsync("0", new object[] { ST, CN, PW, MN, sendValues, DateTime.Now }, 0);
//如果不保持连接,断开连接 //如果不保持连接,断开连接
if (!KeepConnect) if (!KeepConnect)

View File

@@ -16,7 +16,17 @@ namespace Modbus.Net.HJ212
public HJ212Protocol(string ip) public HJ212Protocol(string ip)
: base(0, 0, Endian.BigEndianLsb) : base(0, 0, Endian.BigEndianLsb)
{ {
ProtocolLinker = new HJ212ProtocolLinker(ip, int.Parse(ConfigurationReader.GetValueDirect("TCP:" + ip, "HJ212Port") ?? ConfigurationReader.GetValueDirect("TCP:Modbus", "HJ212Port") ?? "443")); var splitPos = ip.IndexOf(':');
if (splitPos > -1)
{
string realIp = ip.Substring(0, splitPos);
string port = ip.Substring(splitPos + 1);
ProtocolLinker = new HJ212ProtocolLinker(realIp, int.Parse(port));
}
else
{
ProtocolLinker = new HJ212ProtocolLinker(ip, int.Parse(ConfigurationReader.GetValueDirect("TCP:" + ip, "HJ212Port") ?? ConfigurationReader.GetValueDirect("TCP:Modbus", "HJ212Port") ?? "443"));
}
} }
/// <summary> /// <summary>
@@ -43,14 +53,15 @@ namespace Modbus.Net.HJ212
public override byte[] Format(IInputStruct message) public override byte[] Format(IInputStruct message)
{ {
var r_message = (WriteRequestHJ212InputStruct)message; var r_message = (WriteRequestHJ212InputStruct)message;
string formatMessage = "##0633"; string formatMessage = "";
formatMessage += "QN=" + r_message.QN + ";"; formatMessage += "QN=" + r_message.QN + ";";
formatMessage += "ST=" + r_message.ST + ";"; formatMessage += "ST=" + r_message.ST + ";";
formatMessage += "CN=" + r_message.CN + ";"; formatMessage += "CN=" + r_message.CN + ";";
formatMessage += "PW=" + r_message.PW + ";"; formatMessage += "PW=" + r_message.PW + ";";
formatMessage += "MN=" + r_message.MN + ";"; formatMessage += "MN=" + r_message.MN + ";";
formatMessage += "Flag=5;";
formatMessage += "CP=&&"; formatMessage += "CP=&&";
formatMessage += "DateTime=" + r_message.Datetime.ToString("yyyyMMddHHmmss") + ";"; formatMessage += "DataTime=" + r_message.Datatime + ";";
foreach (var record in r_message.CP) foreach (var record in r_message.CP)
{ {
foreach (var data in record) foreach (var data in record)
@@ -61,7 +72,6 @@ namespace Modbus.Net.HJ212
formatMessage += ";"; formatMessage += ";";
} }
formatMessage = formatMessage[..^1]; formatMessage = formatMessage[..^1];
formatMessage += "&&";
return Encoding.ASCII.GetBytes(formatMessage); return Encoding.ASCII.GetBytes(formatMessage);
} }
@@ -84,15 +94,16 @@ namespace Modbus.Net.HJ212
{ {
public WriteRequestHJ212InputStruct(string st, string cn, string pw, string mn, List<Dictionary<string, string>> cp, DateTime datetime) public WriteRequestHJ212InputStruct(string st, string cn, string pw, string mn, List<Dictionary<string, string>> cp, DateTime datetime)
{ {
QN = datetime.ToString("yyyyMMddHHmmssffff");
ST = st; ST = st;
CN = cn; CN = cn;
PW = pw; PW = pw;
MN = mn; MN = mn;
CP = cp; CP = cp;
Datetime = datetime; Datatime = datetime.ToString("yyyyMMddHHmmss");
} }
public string QN => "20170101000926706"; public string QN { get; }
public string ST { get; } public string ST { get; }
@@ -104,7 +115,7 @@ namespace Modbus.Net.HJ212
public List<Dictionary<string, string>> CP { get; } public List<Dictionary<string, string>> CP { get; }
public DateTime Datetime { get; } public string Datatime { get; }
} }
/// <summary> /// <summary>

View File

@@ -1,4 +1,10 @@
namespace Modbus.Net.HJ212 using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Threading.Tasks;
namespace Modbus.Net.HJ212
{ {
/// <summary> /// <summary>
/// HJ212协议连接器 /// HJ212协议连接器
@@ -9,6 +15,45 @@
{ {
} }
public override async Task<byte[]> SendReceiveAsync(byte[] content)
{
if (content.Length <= 1000)
return await base.SendReceiveAsync(content);
else
{
string contentString = Encoding.ASCII.GetString(content);
string[] formats = { "yyyyMMddHHmmssffff", "yyyyMMddHHmmssfff", "yyyyMMddHHmmss" };
DateTime startTime = DateTime.ParseExact(contentString.Substring(3, 18), formats, CultureInfo.InvariantCulture, DateTimeStyles.None);
int dataStartIdx = contentString.IndexOf("&&") + 2;
string head = contentString.Substring(0, dataStartIdx).Substring(21);
string[] contentUnitAll = contentString.Substring(dataStartIdx).Split(';')[1].Split(',');
List<string> contentSplitAll = new List<string>();
string newContent = "DataTime=" + startTime.ToString("yyyyMMddHHmmss") + ";";
foreach (var contentUnit in contentUnitAll)
{
if (newContent.Length + contentUnit.Length > 1000 - dataStartIdx)
{
contentSplitAll.Add("QN=" + startTime.ToString("yyyyMMddHHmmssffff") + head + newContent[..^1]);
startTime = startTime.AddSeconds(1);
newContent = "DataTime=" + startTime.ToString("yyyyMMddHHmmss") + ";" + contentUnit + ",";
}
else
{
newContent += contentUnit + ",";
}
}
contentSplitAll.Add("QN=" + startTime.ToString("yyyyMMddHHmmssffff") + head + newContent[..^1]);
var receiveBytesAll = new List<byte>();
foreach (var contentSplit in contentSplitAll)
{
var extBytes = BytesExtend(Encoding.ASCII.GetBytes(contentSplit));
var receiveBytes = await SendReceiveWithoutExtAndDecAsync(extBytes);
receiveBytesAll.AddRange(receiveBytes == null ? null : receiveBytes.Length == 0 ? receiveBytes : BytesDecact(receiveBytes));
}
return receiveBytesAll.ToArray();
}
}
/// <summary> /// <summary>
/// 检查接收的数据是否正确 /// 检查接收的数据是否正确
/// </summary> /// </summary>
@@ -19,4 +64,4 @@
return true; return true;
} }
} }
} }

View File

@@ -15,14 +15,21 @@ namespace Modbus.Net.HJ212
/// <returns>扩展后的协议内容</returns> /// <returns>扩展后的协议内容</returns>
public byte[] BytesExtend(byte[] content) public byte[] BytesExtend(byte[] content)
{ {
var crc = new byte[2]; var newFormat = new byte[content.Length + 14];
Array.Copy(content, 0, newFormat, 6, content.Length);
//表头长度扩张
var length = content.Length + 2;
string lengthString = length.ToString("0000");
lengthString = "##" + lengthString;
var lengthCalc = Encoding.ASCII.GetBytes(lengthString);
Array.Copy(lengthCalc, 0, newFormat, 0, 6);
//Modbus/Rtu协议扩张增加CRC校验 //Modbus/Rtu协议扩张增加CRC校验
var newFormat = new byte[content.Length + 4]; var crc = new byte[2];
Crc16.GetInstance().GetCRC(content, ref crc); Crc16.GetInstance().GetCRC(content, ref crc);
Array.Copy(content, 0, newFormat, 0, content.Length);
string crcString = BitConverter.ToString(crc).Replace("-", string.Empty); string crcString = BitConverter.ToString(crc).Replace("-", string.Empty);
crcString = "&&" + crcString + "\r\n";
var crcCalc = Encoding.ASCII.GetBytes(crcString); var crcCalc = Encoding.ASCII.GetBytes(crcString);
Array.Copy(crcCalc, 0, newFormat, newFormat.Length - 4, 4); Array.Copy(crcCalc, 0, newFormat, newFormat.Length - 8, 8);
return newFormat; return newFormat;
} }

View File

@@ -17,7 +17,7 @@ namespace Modbus.Net.HJ212
public override Endian Endian => throw new NotImplementedException(); public override Endian Endian => throw new NotImplementedException();
public override Task<ReturnStruct<byte[]>> GetDatasAsync(string startAddress, int getByteCount) public override Task<ReturnStruct<byte[]>> GetDatasAsync(string startAddress, int getByteCount, int getOriginalCount)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
@@ -27,20 +27,20 @@ namespace Modbus.Net.HJ212
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override async Task<ReturnStruct<bool>> SetDatasAsync(string startAddress, object[] setContents) public override async Task<ReturnStruct<bool>> SetDatasAsync(string startAddress, object[] setContents, int setOriginalCount)
{ {
try try
{ {
var writeRequestHJ212InputStruct = var writeRequestHJ212InputStruct =
new WriteRequestHJ212InputStruct((string)setContents[0], (string)setContents[1], (string)setContents[2], (string)setContents[3], (List<Dictionary<string, string>>)setContents[4], (DateTime)setContents[5]); new WriteRequestHJ212InputStruct((string)setContents[0], (string)setContents[1], (string)setContents[2], (string)setContents[3], (List<Dictionary<string, string>>)setContents[4], (DateTime)setContents[5]);
var writeRequestOpcOutputStruct = var writeRequestHJ212OutputStruct =
await await
Wrapper.SendReceiveAsync<WriteRequestHJ212OutputStruct>(Wrapper[typeof(WriteRequestHJ212Protocol)], Wrapper.SendReceiveAsync<WriteRequestHJ212OutputStruct>(Wrapper[typeof(WriteRequestHJ212Protocol)],
writeRequestHJ212InputStruct); writeRequestHJ212InputStruct);
return new ReturnStruct<bool> return new ReturnStruct<bool>
{ {
Datas = writeRequestOpcOutputStruct?.GetValue != null, Datas = writeRequestHJ212OutputStruct?.GetValue != null,
IsSuccess = writeRequestOpcOutputStruct?.GetValue != null, IsSuccess = writeRequestHJ212OutputStruct?.GetValue != null,
ErrorCode = 0, ErrorCode = 0,
ErrorMsg = null, ErrorMsg = null,
}; };

View File

@@ -33,8 +33,7 @@ namespace Modbus.Net.Modbus
/// <returns>校验是否正确</returns> /// <returns>校验是否正确</returns>
public override bool? CheckRight(byte[] content) public override bool? CheckRight(byte[] content)
{ {
//ProtocolLinker不会返回null if (base.CheckRight(content) != true) return base.CheckRight(content);
if (base.CheckRight(content) != true) return false;
//Modbus协议错误 //Modbus协议错误
var contentString = Encoding.ASCII.GetString(content); var contentString = Encoding.ASCII.GetString(content);
if (byte.Parse(contentString.Substring(3, 2)) > 127) if (byte.Parse(contentString.Substring(3, 2)) > 127)

View File

@@ -33,8 +33,7 @@ namespace Modbus.Net.Modbus
/// <returns>校验是否正确</returns> /// <returns>校验是否正确</returns>
public override bool? CheckRight(byte[] content) public override bool? CheckRight(byte[] content)
{ {
//ProtocolLinker不会返回null if (base.CheckRight(content) != true) return base.CheckRight(content);
if (base.CheckRight(content) != true) return false;
//Modbus协议错误 //Modbus协议错误
var contentString = Encoding.ASCII.GetString(content); var contentString = Encoding.ASCII.GetString(content);
if (byte.Parse(contentString.Substring(3, 2)) > 127) if (byte.Parse(contentString.Substring(3, 2)) > 127)

View File

@@ -24,8 +24,7 @@ namespace Modbus.Net.Modbus
/// <returns>校验是否正确</returns> /// <returns>校验是否正确</returns>
public override bool? CheckRight(byte[] content) public override bool? CheckRight(byte[] content)
{ {
//ProtocolLinker不会返回null if (base.CheckRight(content) != true) return base.CheckRight(content);
if (base.CheckRight(content) != true) return false;
//Modbus协议错误 //Modbus协议错误
var contentString = Encoding.ASCII.GetString(content); var contentString = Encoding.ASCII.GetString(content);
if (byte.Parse(contentString.Substring(3, 2)) > 127) if (byte.Parse(contentString.Substring(3, 2)) > 127)

View File

@@ -23,9 +23,9 @@ namespace Modbus.Net.Modbus
}; };
/// <summary> /// <summary>
/// Modbus Rtu协议长度计算 /// Modbus Rtu接收协议长度计算
/// </summary> /// </summary>
public static Func<byte[], int> ModbusRtuLengthCalc => content => public static Func<byte[], int> ModbusRtuResponseLengthCalc => (content) =>
{ {
if (content[1] > 128) return 5; if (content[1] > 128) return 5;
else if (content[1] == 5 || content[1] == 6 || content[1] == 8 || content[1] == 11 || content[1] == 15 || content[1] == 16) return 8; else if (content[1] == 5 || content[1] == 6 || content[1] == 8 || content[1] == 11 || content[1] == 15 || content[1] == 16) return 8;
@@ -33,6 +33,20 @@ namespace Modbus.Net.Modbus
else if (content[1] == 22) return 10; else if (content[1] == 22) return 10;
else return DuplicateWithCount.GetDuplcateFunc(new List<int> { 2 }, 5).Invoke(content); else return DuplicateWithCount.GetDuplcateFunc(new List<int> { 2 }, 5).Invoke(content);
}; };
/// <summary>
/// Modbus Rtu发送协议长度计算
/// </summary>
public static Func<byte[], int> ModbusRtuRequestLengthCalc => (content) =>
{
if (content[1] == 1 || content[1] == 2 || content[1] == 3 || content[1] == 4 || content[1] == 5 || content[1] == 6 || content[1] == 8) return 8;
else if (content[1] == 7 || content[1] == 11 || content[1] == 12 || content[1] == 17) return 4;
else if (content[1] == 15 || content[1] == 16) { return DuplicateWithCount.GetDuplcateFunc(new List<int> { 6 }, 9).Invoke(content); }
else if (content[1] == 22) return 10;
else if (content[1] == 23) return 19;
else if (content[1] == 24) return 6;
else return DuplicateWithCount.GetDuplcateFunc(new List<int> { 2 }, 5).Invoke(content);
};
} }
/// <summary> /// <summary>
@@ -97,7 +111,7 @@ namespace Modbus.Net.Modbus
} }
/// <summary> /// <summary>
/// Modbus Rtu协议控制器 /// Modbus Rtu发送协议控制器
/// </summary> /// </summary>
public class ModbusRtuController : FifoController public class ModbusRtuController : FifoController
{ {
@@ -108,7 +122,7 @@ namespace Modbus.Net.Modbus
/// <param name="slaveAddress">从站号</param> /// <param name="slaveAddress">从站号</param>
public ModbusRtuController(string com, int slaveAddress) : base( public ModbusRtuController(string com, int slaveAddress) : base(
int.Parse(ConfigurationReader.GetValue("COM:" + com + ":" + slaveAddress, "FetchSleepTime")), int.Parse(ConfigurationReader.GetValue("COM:" + com + ":" + slaveAddress, "FetchSleepTime")),
lengthCalc: ModbusLengthCalc.ModbusRtuLengthCalc, lengthCalc: ModbusLengthCalc.ModbusRtuResponseLengthCalc,
checkRightFunc: ContentCheck.Crc16CheckRight, checkRightFunc: ContentCheck.Crc16CheckRight,
waitingListMaxCount: ConfigurationReader.GetValue("COM:" + com + ":" + slaveAddress, "WaitingListCount") != null ? waitingListMaxCount: ConfigurationReader.GetValue("COM:" + com + ":" + slaveAddress, "WaitingListCount") != null ?
int.Parse(ConfigurationReader.GetValue("COM:" + com + ":" + slaveAddress, "WaitingListCount")) : int.Parse(ConfigurationReader.GetValue("COM:" + com + ":" + slaveAddress, "WaitingListCount")) :
@@ -118,7 +132,28 @@ namespace Modbus.Net.Modbus
} }
/// <summary> /// <summary>
/// Modbus Rtu in Tcp协议控制器 /// Modbus Rtu接收协议控制器
/// </summary>
public class ModbusRtuResponseController : FifoController
{
/// <summary>
/// 构造函数
/// </summary>
/// <param name="com">串口</param>
/// <param name="slaveAddress">从站号</param>
public ModbusRtuResponseController(string com, int slaveAddress) : base(
int.Parse(ConfigurationReader.GetValue("COM:" + com + ":" + slaveAddress, "FetchSleepTime")),
lengthCalc: ModbusLengthCalc.ModbusRtuRequestLengthCalc,
checkRightFunc: ContentCheck.Crc16CheckRight,
waitingListMaxCount: ConfigurationReader.GetValue("COM:" + com + ":" + slaveAddress, "WaitingListCount") != null ?
int.Parse(ConfigurationReader.GetValue("COM:" + com + ":" + slaveAddress, "WaitingListCount")) :
null
)
{ }
}
/// <summary>
/// Modbus Rtu in Tcp发送协议控制器
/// </summary> /// </summary>
public class ModbusRtuInTcpController : FifoController public class ModbusRtuInTcpController : FifoController
{ {
@@ -129,7 +164,7 @@ namespace Modbus.Net.Modbus
/// <param name="port">端口号</param> /// <param name="port">端口号</param>
public ModbusRtuInTcpController(string ip, int port) : base( public ModbusRtuInTcpController(string ip, int port) : base(
int.Parse(ConfigurationReader.GetValue("TCP:" + ip + ":" + port, "FetchSleepTime")), int.Parse(ConfigurationReader.GetValue("TCP:" + ip + ":" + port, "FetchSleepTime")),
lengthCalc: ModbusLengthCalc.ModbusRtuLengthCalc, lengthCalc: ModbusLengthCalc.ModbusRtuResponseLengthCalc,
checkRightFunc: ContentCheck.Crc16CheckRight, checkRightFunc: ContentCheck.Crc16CheckRight,
waitingListMaxCount: ConfigurationReader.GetValue("TCP:" + ip + ":" + port, "WaitingListCount") != null ? waitingListMaxCount: ConfigurationReader.GetValue("TCP:" + ip + ":" + port, "WaitingListCount") != null ?
int.Parse(ConfigurationReader.GetValue("TCP:" + ip + ":" + port, "WaitingListCount")) : int.Parse(ConfigurationReader.GetValue("TCP:" + ip + ":" + port, "WaitingListCount")) :
@@ -139,7 +174,28 @@ namespace Modbus.Net.Modbus
} }
/// <summary> /// <summary>
/// Modbus Rtu in Udp协议控制器 /// Modbus Rtu in Tcp接收协议控制器
/// </summary>
public class ModbusRtuInTcpResponseController : FifoController
{
/// <summary>
/// 构造函数
/// </summary>
/// <param name="ip">ip地址</param>
/// <param name="port">端口号</param>
public ModbusRtuInTcpResponseController(string ip, int port) : base(
int.Parse(ConfigurationReader.GetValue("TCP:" + ip + ":" + port, "FetchSleepTime")),
lengthCalc: ModbusLengthCalc.ModbusRtuRequestLengthCalc,
checkRightFunc: ContentCheck.Crc16CheckRight,
waitingListMaxCount: ConfigurationReader.GetValue("TCP:" + ip + ":" + port, "WaitingListCount") != null ?
int.Parse(ConfigurationReader.GetValue("TCP:" + ip + ":" + port, "WaitingListCount")) :
null
)
{ }
}
/// <summary>
/// Modbus Rtu in Udp发送协议控制器
/// </summary> /// </summary>
public class ModbusRtuInUdpController : FifoController public class ModbusRtuInUdpController : FifoController
{ {
@@ -150,7 +206,28 @@ namespace Modbus.Net.Modbus
/// <param name="port">端口号</param> /// <param name="port">端口号</param>
public ModbusRtuInUdpController(string ip, int port) : base( public ModbusRtuInUdpController(string ip, int port) : base(
int.Parse(ConfigurationReader.GetValue("UDP:" + ip + ":" + port, "FetchSleepTime")), int.Parse(ConfigurationReader.GetValue("UDP:" + ip + ":" + port, "FetchSleepTime")),
lengthCalc: ModbusLengthCalc.ModbusRtuLengthCalc, lengthCalc: ModbusLengthCalc.ModbusRtuResponseLengthCalc,
checkRightFunc: ContentCheck.Crc16CheckRight,
waitingListMaxCount: ConfigurationReader.GetValue("UDP:" + ip + ":" + port, "WaitingListCount") != null ?
int.Parse(ConfigurationReader.GetValue("UDP:" + ip + ":" + port, "WaitingListCount")) :
null
)
{ }
}
/// <summary>
/// Modbus Rtu in Udp接收协议控制器
/// </summary>
public class ModbusRtuInUdpResponseController : FifoController
{
/// <summary>
/// 构造函数
/// </summary>
/// <param name="ip">ip地址</param>
/// <param name="port">端口号</param>
public ModbusRtuInUdpResponseController(string ip, int port) : base(
int.Parse(ConfigurationReader.GetValue("UDP:" + ip + ":" + port, "FetchSleepTime")),
lengthCalc: ModbusLengthCalc.ModbusRtuRequestLengthCalc,
checkRightFunc: ContentCheck.Crc16CheckRight, checkRightFunc: ContentCheck.Crc16CheckRight,
waitingListMaxCount: ConfigurationReader.GetValue("UDP:" + ip + ":" + port, "WaitingListCount") != null ? waitingListMaxCount: ConfigurationReader.GetValue("UDP:" + ip + ":" + port, "WaitingListCount") != null ?
int.Parse(ConfigurationReader.GetValue("UDP:" + ip + ":" + port, "WaitingListCount")) : int.Parse(ConfigurationReader.GetValue("UDP:" + ip + ":" + port, "WaitingListCount")) :

View File

@@ -20,10 +20,10 @@ namespace Modbus.Net.Modbus
/// <param name="slaveAddress">从站号</param> /// <param name="slaveAddress">从站号</param>
/// <param name="masterAddress">主站号</param> /// <param name="masterAddress">主站号</param>
/// <param name="endian">端格式</param> /// <param name="endian">端格式</param>
public ModbusMachine(TKey id, ModbusType connectionType, string connectionString, public ModbusMachine(TKey id, string alias, ModbusType connectionType, string connectionString,
IEnumerable<AddressUnit<TUnitKey, int, int>> getAddresses, bool keepConnect, byte slaveAddress, byte masterAddress, IEnumerable<AddressUnit<TUnitKey, int, int>> getAddresses, bool keepConnect, byte slaveAddress, byte masterAddress,
Endian endian) Endian endian)
: base(id, getAddresses, keepConnect, slaveAddress, masterAddress) : base(id, alias, getAddresses, keepConnect, slaveAddress, masterAddress)
{ {
BaseUtility = new ModbusUtility(connectionType, connectionString, slaveAddress, masterAddress, endian); BaseUtility = new ModbusUtility(connectionType, connectionString, slaveAddress, masterAddress, endian);
AddressFormater = new AddressFormaterModbus(); AddressFormater = new AddressFormaterModbus();
@@ -41,10 +41,10 @@ namespace Modbus.Net.Modbus
/// <param name="slaveAddress">从站号</param> /// <param name="slaveAddress">从站号</param>
/// <param name="masterAddress">主站号</param> /// <param name="masterAddress">主站号</param>
/// <param name="endian">端格式</param> /// <param name="endian">端格式</param>
public ModbusMachine(TKey id, ModbusType connectionType, string connectionString, public ModbusMachine(TKey id, string alias, ModbusType connectionType, string connectionString,
IEnumerable<AddressUnit<TUnitKey, int, int>> getAddresses, byte slaveAddress, byte masterAddress, IEnumerable<AddressUnit<TUnitKey, int, int>> getAddresses, byte slaveAddress, byte masterAddress,
Endian endian) Endian endian)
: this(id, connectionType, connectionString, getAddresses, true, slaveAddress, masterAddress, endian) : this(id, alias, connectionType, connectionString, getAddresses, true, slaveAddress, masterAddress, endian)
{ {
} }
} }

View File

@@ -146,17 +146,18 @@ namespace Modbus.Net.Modbus
/// </summary> /// </summary>
/// <param name="slaveAddress">从站地址</param> /// <param name="slaveAddress">从站地址</param>
/// <param name="startAddress">开始地址</param> /// <param name="startAddress">开始地址</param>
/// <param name="getCount">读取个数</param> /// <param name="getByteCount">读取字节个数</param>
/// <param name="getOriginalCount">读取原始个数</param>
/// <param name="addressTranslator">地址翻译器</param> /// <param name="addressTranslator">地址翻译器</param>
public ReadDataModbusInputStruct(byte slaveAddress, string startAddress, ushort getCount, public ReadDataModbusInputStruct(byte slaveAddress, string startAddress, ushort getByteCount,
AddressTranslator addressTranslator) AddressTranslator addressTranslator, ushort getOriginalCount = 0)
{ {
SlaveAddress = slaveAddress; SlaveAddress = slaveAddress;
var translateAddress = addressTranslator.AddressTranslate(startAddress, true); var translateAddress = addressTranslator.AddressTranslate(startAddress, true);
FunctionCode = (byte)translateAddress.Area; FunctionCode = (byte)translateAddress.Area;
StartAddress = (ushort)translateAddress.Address; StartAddress = (ushort)translateAddress.Address;
GetCount = GetCount = translateAddress.AreaString == "0X" || translateAddress.AreaString == "1X" ? getOriginalCount :
(ushort)Math.Ceiling(getCount / addressTranslator.GetAreaByteLength(translateAddress.AreaString)); (ushort)Math.Ceiling(getByteCount / addressTranslator.GetAreaByteLength(translateAddress.AreaString));
} }
/// <summary> /// <summary>
@@ -273,8 +274,9 @@ namespace Modbus.Net.Modbus
/// <param name="writeValue">写入的数据</param> /// <param name="writeValue">写入的数据</param>
/// <param name="addressTranslator">地址翻译器</param> /// <param name="addressTranslator">地址翻译器</param>
/// <param name="endian">端格式</param> /// <param name="endian">端格式</param>
/// <param name="setOriginalCount">设置原始长度</param>
public WriteDataModbusInputStruct(byte slaveAddress, string startAddress, object[] writeValue, public WriteDataModbusInputStruct(byte slaveAddress, string startAddress, object[] writeValue,
AddressTranslator addressTranslator, Endian endian) AddressTranslator addressTranslator, Endian endian, ushort setOriginalCount = 0)
{ {
SlaveAddress = slaveAddress; SlaveAddress = slaveAddress;
var translateAddress = addressTranslator.AddressTranslate(startAddress, false); var translateAddress = addressTranslator.AddressTranslate(startAddress, false);
@@ -282,7 +284,7 @@ namespace Modbus.Net.Modbus
StartAddress = (ushort)translateAddress.Address; StartAddress = (ushort)translateAddress.Address;
var writeByteValue = ValueHelper.GetInstance(endian).ObjectArrayToByteArray(writeValue); var writeByteValue = ValueHelper.GetInstance(endian).ObjectArrayToByteArray(writeValue);
if (writeByteValue.Length % 2 == 1) writeByteValue = writeByteValue.ToList().Append<byte>(0).ToArray(); if (writeByteValue.Length % 2 == 1) writeByteValue = writeByteValue.ToList().Append<byte>(0).ToArray();
WriteCount = WriteCount = translateAddress.AreaString == "0X" || translateAddress.AreaString == "1X" ? setOriginalCount :
(ushort)(writeByteValue.Length / addressTranslator.GetAreaByteLength(translateAddress.AreaString)); (ushort)(writeByteValue.Length / addressTranslator.GetAreaByteLength(translateAddress.AreaString));
WriteByteCount = (byte)writeByteValue.Length; WriteByteCount = (byte)writeByteValue.Length;
WriteValue = writeByteValue; WriteValue = writeByteValue;

View File

@@ -169,4 +169,9 @@ namespace Modbus.Net.Modbus
return newContent.ToArray(); return newContent.ToArray();
} }
} }
public class ModbusRtuProtocolReceiverBytesExtend : ModbusRtuProtocolLinkerBytesExtend
{
}
} }

View File

@@ -0,0 +1,214 @@
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection.Metadata.Ecma335;
using System.Threading.Tasks;
using AddressUnit = Modbus.Net.AddressUnit<string, int, int>;
using DataReturnDef = Modbus.Net.DataReturnDef<string, double>;
namespace Modbus.Net.Modbus
{
public class ModbusRtuDataReceiver
{
private Dictionary<ModbusRtuProtocolReceiver, DateTime> _receivers;
private readonly IConfigurationRoot configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ?? "Production"}.json", true)
.Build();
private bool[] zerox = new bool[10000];
private byte[] threex = new byte[20000];
public delegate Dictionary<string, double> ReturnValuesDelegate(DataReturnDef returnValues);
public event ReturnValuesDelegate ReturnValueDictionary;
protected void AddValueToValueDic(Dictionary<string, double> valueDic, Dictionary<string, ReturnUnit<double>> returnDic, AddressUnit address, double value, MachineDataType dataType)
{
switch (dataType)
{
case MachineDataType.Id:
{
valueDic.Add(address.Id, value);
returnDic.Add(address.Id, new ReturnUnit<double>() { AddressUnit = address.MapAddressUnitTUnitKeyToAddressUnit(), DeviceValue = value });
break;
}
case MachineDataType.Name:
{
valueDic.Add(address.Name, value);
returnDic.Add(address.Name, new ReturnUnit<double>() { AddressUnit = address.MapAddressUnitTUnitKeyToAddressUnit(), DeviceValue = value });
break;
}
case MachineDataType.Address:
{
valueDic.Add(new AddressFormaterModbus().FormatAddress(address.Area, address.Address, address.SubAddress), value);
returnDic.Add(new AddressFormaterModbus().FormatAddress(address.Area, address.Address, address.SubAddress), new ReturnUnit<double>() { AddressUnit = address.MapAddressUnitTUnitKeyToAddressUnit(), DeviceValue = value });
break;
}
case MachineDataType.CommunicationTag:
{
valueDic.Add(address.CommunicationTag, value);
returnDic.Add(address.CommunicationTag, new ReturnUnit<double>() { AddressUnit = address.MapAddressUnitTUnitKeyToAddressUnit(), DeviceValue = value });
break;
}
}
}
public ModbusRtuDataReceiver(MachineDataType dataType, int minimumElapse = 0)
{
_receivers = new Dictionary<ModbusRtuProtocolReceiver,DateTime>();
var receiversDef = configuration.GetSection("Modbus.Net").GetSection("Receiver").GetChildren();
foreach (var receiverDef in receiversDef)
{
var machineName = receiverDef.GetValue<string>("a:id");
var _receiver = new ModbusRtuProtocolReceiver(receiverDef.GetValue<string>("e:connectionString"), receiverDef.GetValue<int>("h:slaveAddress"));
var addressMapName = receiverDef.GetValue<string>("f:addressMap");
var endian = ValueHelper.GetInstance(Endian.Parse(receiverDef.GetValue<string>("j:endian")));
_receiver.DataProcess = receiveContent =>
{
var returnTime = DateTime.Now;
byte[] returnBytes = null;
var readContent = new byte[receiveContent.Count * 2];
var values = receiveContent.WriteContent;
var valueDic = new Dictionary<string, double>();
var returnDic = new Dictionary<string, ReturnUnit<double>>();
List<AddressUnit> addressMap = AddressReader<string, int, int>.ReadAddresses(addressMapName).ToList();
if (values != null)
{
switch (receiveContent.FunctionCode)
{
case (byte)ModbusProtocolFunctionCode.WriteMultiRegister:
{
Array.Copy(receiveContent.WriteContent, 0, threex, receiveContent.StartAddress * 2, receiveContent.WriteContent.Length);
returnBytes = new WriteDataModbusProtocol().Format(receiveContent.SlaveAddress, receiveContent.FunctionCode, receiveContent.StartAddress, receiveContent.Count);
break;
}
case (byte)ModbusProtocolFunctionCode.WriteSingleCoil:
{
if (receiveContent.WriteContent[0] == 255)
{
zerox[receiveContent.StartAddress] = true;
}
else
{
zerox[receiveContent.StartAddress] = false;
}
returnBytes = new WriteDataModbusProtocol().Format(receiveContent.SlaveAddress, receiveContent.FunctionCode, receiveContent.StartAddress, receiveContent.WriteContent);
break;
}
case (byte)ModbusProtocolFunctionCode.WriteMultiCoil:
{
var pos = 0;
List<bool> bitList = new List<bool>();
for (int i = 0; i < receiveContent.WriteByteCount; i++)
{
var bitArray = endian.GetBits(receiveContent.WriteContent, ref pos);
bitList.AddRange(bitArray.ToList());
}
Array.Copy(bitList.ToArray(), 0, zerox, receiveContent.StartAddress, bitList.Count);
returnBytes = new WriteDataModbusProtocol().Format(receiveContent.SlaveAddress, receiveContent.FunctionCode, receiveContent.StartAddress, receiveContent.Count);
break;
}
case (byte)ModbusProtocolFunctionCode.WriteSingleRegister:
{
Array.Copy(receiveContent.WriteContent, 0, threex, receiveContent.StartAddress * 2, receiveContent.Count * 2);
returnBytes = new WriteDataModbusProtocol().Format(receiveContent.SlaveAddress, receiveContent.FunctionCode, receiveContent.StartAddress, receiveContent.Count);
break;
}
}
try
{
for (int i = 0; i < addressMap.Count; i++)
{
var pos = (addressMap[i].Address - 1) * 2;
var subpos = addressMap[i].SubAddress;
string valueString = null;
if (addressMap[i].Area == "4X")
{
valueString = endian.GetValue(threex, ref pos, ref subpos, addressMap[i].DataType).ToString();
}
else if (addressMap[i].Area == "0X")
{
valueString = zerox[addressMap[i].Address - 1].ToString();
}
if (valueString == "True") valueString = "1";
if (valueString == "False") valueString = "0";
var value = double.Parse(valueString);
value = value * addressMap[i].Zoom;
value = Math.Round(value, addressMap[i].DecimalPos);
AddValueToValueDic(valueDic, returnDic, addressMap[i], value, dataType);
}
if (machineName == "EventData" || (returnTime - _receivers[_receiver]).TotalSeconds + 0.5 >= minimumElapse)
{
if (ReturnValueDictionary != null)
{
var dataReturn = new DataReturnDef();
dataReturn.MachineId = machineName;
dataReturn.ReturnValues = new ReturnStruct<Dictionary<string, ReturnUnit<double>>>() { IsSuccess = true, Datas = returnDic };
ReturnValueDictionary(dataReturn);
_receivers[_receiver] = returnTime;
}
}
}
catch (Exception ex)
{
//_logger.LogError(ex, "Error");
}
}
else
{
switch (receiveContent.FunctionCode)
{
case (byte)ModbusProtocolFunctionCode.ReadHoldRegister:
{
Array.Copy(threex, receiveContent.StartAddress, readContent, 0, readContent.Length);
returnBytes = new ReadDataModbusProtocol().Format(receiveContent.SlaveAddress, receiveContent.FunctionCode, (byte)receiveContent.Count, readContent);
break;
}
case (byte)ModbusProtocolFunctionCode.ReadCoilStatus:
{
var bitCount = receiveContent.WriteByteCount * 8;
var boolContent = new bool[bitCount];
Array.Copy(zerox, receiveContent.StartAddress, boolContent, 0, bitCount);
var byteList = new List<byte>();
for (int i = 0; i < receiveContent.WriteByteCount; i++)
{
byte result = 0;
for (int j = i; j < i + 8; j++)
{
// 将布尔值转换为对应的位
byte bit = boolContent[j] ? (byte)1 : (byte)0;
// 使用左移位运算将位合并到结果字节中
result = (byte)((result << 1) | bit);
}
byteList.Add(result);
}
readContent = byteList.ToArray();
returnBytes = new ReadDataModbusProtocol().Format(receiveContent.SlaveAddress, receiveContent.FunctionCode, receiveContent.WriteByteCount, readContent);
break;
}
}
}
if (returnBytes != null) return returnBytes;
else return null;
};
_receivers.Add(_receiver, DateTime.MinValue);
}
}
public async Task<bool> ConnectAsync()
{
var result = await Task.FromResult(Parallel.ForEach(_receivers, async _receiver =>
{
await _receiver.Key.ConnectAsync();
}));
return result.IsCompleted;
}
}
}

View File

@@ -31,8 +31,7 @@
/// <returns>数据是否正确</returns> /// <returns>数据是否正确</returns>
public override bool? CheckRight(byte[] content) public override bool? CheckRight(byte[] content)
{ {
//ProtocolLinker的CheckRight不会返回null if (base.CheckRight(content) != true) return base.CheckRight(content);
if (base.CheckRight(content) != true) return false;
//Modbus协议错误 //Modbus协议错误
if (content[1] > 127) if (content[1] > 127)
throw new ModbusProtocolErrorException(content[2]); throw new ModbusProtocolErrorException(content[2]);

View File

@@ -31,8 +31,7 @@
/// <returns>数据是否正确</returns> /// <returns>数据是否正确</returns>
public override bool? CheckRight(byte[] content) public override bool? CheckRight(byte[] content)
{ {
//ProtocolLinker的CheckRight不会返回null if (base.CheckRight(content) != true) return base.CheckRight(content);
if (base.CheckRight(content) != true) return false;
//Modbus协议错误 //Modbus协议错误
if (content[1] > 127) if (content[1] > 127)
throw new ModbusProtocolErrorException(content[2]); throw new ModbusProtocolErrorException(content[2]);

View File

@@ -22,8 +22,7 @@
/// <returns>数据是否正确</returns> /// <returns>数据是否正确</returns>
public override bool? CheckRight(byte[] content) public override bool? CheckRight(byte[] content)
{ {
//ProtocolLinker的CheckRight不会返回null if (base.CheckRight(content) != true) return base.CheckRight(content);
if (base.CheckRight(content) != true) return false;
//CRC校验失败 //CRC校验失败
if (!Crc16.GetInstance().CrcEfficacy(content)) if (!Crc16.GetInstance().CrcEfficacy(content))
throw new ModbusProtocolErrorException(501); throw new ModbusProtocolErrorException(501);

View File

@@ -0,0 +1,67 @@
using System;
namespace Modbus.Net.Modbus
{
public class ModbusRtuProtocolReceiver : ProtocolReceiver
{
public ModbusRtuProtocolReceiver(string com, int slaveAddress)
: base(com, slaveAddress)
{
}
protected override Func<byte[], ReceiveDataDef> DataExplain
{
get
{
return receiveBytes =>
{
var writeContent = receiveBytes.Length > 6 ? new byte[receiveBytes.Length - 7] : null;
if (receiveBytes.Length > 6)
{
Array.Copy(receiveBytes, 7, writeContent, 0, receiveBytes.Length - 7);
return new ReceiveDataDef()
{
SlaveAddress = receiveBytes[0],
FunctionCode = receiveBytes[1],
StartAddress = (ushort)(receiveBytes[2] * 256 + receiveBytes[3]),
Count = (ushort)(receiveBytes[4] * 256 + receiveBytes[5]),
WriteByteCount = (byte)(receiveBytes.Length > 6 ? receiveBytes[6] : 0),
WriteContent = writeContent
};
}
else if (receiveBytes.Length == 6)
{
if (receiveBytes[1] == 1 || receiveBytes[1] == 3)
{
writeContent = null;
return new ReceiveDataDef()
{
SlaveAddress = receiveBytes[0],
FunctionCode = receiveBytes[1],
StartAddress = (ushort)(receiveBytes[2] * 256 + receiveBytes[3]),
Count = (ushort)(receiveBytes[4] * 256 + receiveBytes[5]),
WriteByteCount = 2,
WriteContent = writeContent
};
}
else
{
writeContent = new byte[2] { receiveBytes[4], receiveBytes[5] };
return new ReceiveDataDef()
{
SlaveAddress = receiveBytes[0],
FunctionCode = receiveBytes[1],
StartAddress = (ushort)(receiveBytes[2] * 256 + receiveBytes[3]),
Count = 1,
WriteByteCount = 2,
WriteContent = writeContent
};
}
}
else return null;
};
}
}
}
}

View File

@@ -30,8 +30,7 @@
/// <returns>数据是否正确</returns> /// <returns>数据是否正确</returns>
public override bool? CheckRight(byte[] content) public override bool? CheckRight(byte[] content)
{ {
//ProtocolLinker的CheckRight不会返回null if (base.CheckRight(content) != true) return base.CheckRight(content);
if (base.CheckRight(content) != true) return false;
//Modbus协议错误 //Modbus协议错误
if (content[7] > 127) if (content[7] > 127)
throw new ModbusProtocolErrorException(content[2] > 0 ? content[2] : content[8]); throw new ModbusProtocolErrorException(content[2] > 0 ? content[2] : content[8]);

View File

@@ -30,8 +30,7 @@
/// <returns>数据是否正确</returns> /// <returns>数据是否正确</returns>
public override bool? CheckRight(byte[] content) public override bool? CheckRight(byte[] content)
{ {
//ProtocolLinker的CheckRight不会返回null if (base.CheckRight(content) != true) return base.CheckRight(content);
if (base.CheckRight(content) != true) return false;
//Modbus协议错误 //Modbus协议错误
if (content[7] > 127) if (content[7] > 127)
throw new ModbusProtocolErrorException(content[2] > 0 ? content[2] : content[8]); throw new ModbusProtocolErrorException(content[2] > 0 ? content[2] : content[8]);

View File

@@ -252,19 +252,19 @@ namespace Modbus.Net.Modbus
} }
/// <inheritdoc /> /// <inheritdoc />
public override async Task<ReturnStruct<byte[]>> GetDatasAsync(string startAddress, int getByteCount) public override async Task<ReturnStruct<byte[]>> GetDatasAsync(string startAddress, int getByteCount, int getOriginalCount)
{ {
try try
{ {
var inputStruct = new ReadDataModbusInputStruct(SlaveAddress, startAddress, var inputStruct = new ReadDataModbusInputStruct(SlaveAddress, startAddress,
(ushort)getByteCount, AddressTranslator); (ushort)getByteCount, AddressTranslator, (ushort)getOriginalCount);
var outputStruct = await var outputStruct = await
Wrapper.SendReceiveAsync<ReadDataModbusOutputStruct>(Wrapper[typeof(ReadDataModbusProtocol)], Wrapper.SendReceiveAsync<ReadDataModbusOutputStruct>(Wrapper[typeof(ReadDataModbusProtocol)],
inputStruct); inputStruct);
return new ReturnStruct<byte[]> return new ReturnStruct<byte[]>
{ {
Datas = outputStruct?.DataValue, Datas = outputStruct?.DataValue,
IsSuccess = true, IsSuccess = outputStruct == null ? null : true,
ErrorCode = 0, ErrorCode = 0,
ErrorMsg = "" ErrorMsg = ""
}; };
@@ -283,21 +283,22 @@ namespace Modbus.Net.Modbus
} }
/// <inheritdoc /> /// <inheritdoc />
public override async Task<ReturnStruct<bool>> SetDatasAsync(string startAddress, object[] setContents) public override async Task<ReturnStruct<bool>> SetDatasAsync(string startAddress, object[] setContents, int setOriginalCount)
{ {
try try
{ {
var inputStruct = new WriteDataModbusInputStruct(SlaveAddress, startAddress, setContents, var inputStruct = new WriteDataModbusInputStruct(SlaveAddress, startAddress, setContents,
AddressTranslator, Endian); AddressTranslator, Endian, (ushort)setOriginalCount);
var outputStruct = await var outputStruct = await
Wrapper.SendReceiveAsync<WriteDataModbusOutputStruct>(Wrapper[typeof(WriteDataModbusProtocol)], Wrapper.SendReceiveAsync<WriteDataModbusOutputStruct>(Wrapper[typeof(WriteDataModbusProtocol)],
inputStruct); inputStruct);
var ans = outputStruct?.WriteCount * 2 == BigEndianLsbValueHelper.Instance.ObjectArrayToByteArray(setContents).Length;
return new ReturnStruct<bool>() return new ReturnStruct<bool>()
{ {
Datas = outputStruct?.WriteCount == setContents.Length, Datas = ans,
IsSuccess = outputStruct?.WriteCount == setContents.Length, IsSuccess = ans,
ErrorCode = outputStruct?.WriteCount == setContents.Length ? 0 : -2, ErrorCode = ans ? 0 : -2,
ErrorMsg = outputStruct?.WriteCount == setContents.Length ? "" : "Data length mismatch" ErrorMsg = ans ? "" : "Data length mismatch"
}; };
} }
catch (ModbusProtocolErrorException e) catch (ModbusProtocolErrorException e)
@@ -325,7 +326,7 @@ namespace Modbus.Net.Modbus
return new ReturnStruct<byte>() return new ReturnStruct<byte>()
{ {
Datas = outputStruct.OutputData, Datas = outputStruct.OutputData,
IsSuccess = true, IsSuccess = outputStruct == null ? null : true,
ErrorCode = 0, ErrorCode = 0,
ErrorMsg = null ErrorMsg = null
}; };
@@ -355,7 +356,7 @@ namespace Modbus.Net.Modbus
return new ReturnStruct<DiagnoticsData>() return new ReturnStruct<DiagnoticsData>()
{ {
Datas = new DiagnoticsData() { SubFunction = outputStruct.SubFunction, Data = outputStruct.Data }, Datas = new DiagnoticsData() { SubFunction = outputStruct.SubFunction, Data = outputStruct.Data },
IsSuccess = true, IsSuccess = outputStruct == null ? null : true,
ErrorCode = 0, ErrorCode = 0,
ErrorMsg = null ErrorMsg = null
}; };
@@ -385,7 +386,7 @@ namespace Modbus.Net.Modbus
return new ReturnStruct<CommEventCounterData>() return new ReturnStruct<CommEventCounterData>()
{ {
Datas = new CommEventCounterData() { EventCount = outputStruct.EventCount, Status = outputStruct.Status }, Datas = new CommEventCounterData() { EventCount = outputStruct.EventCount, Status = outputStruct.Status },
IsSuccess = true, IsSuccess = outputStruct == null ? null : true,
ErrorCode = 0, ErrorCode = 0,
ErrorMsg = null ErrorMsg = null
}; };
@@ -415,7 +416,7 @@ namespace Modbus.Net.Modbus
return new ReturnStruct<CommEventLogData>() return new ReturnStruct<CommEventLogData>()
{ {
Datas = new CommEventLogData() { Status = outputStruct.Status, Events = outputStruct.Events }, Datas = new CommEventLogData() { Status = outputStruct.Status, Events = outputStruct.Events },
IsSuccess = true, IsSuccess = outputStruct == null ? null : true,
ErrorCode = 0, ErrorCode = 0,
ErrorMsg = null ErrorMsg = null
}; };
@@ -445,7 +446,7 @@ namespace Modbus.Net.Modbus
return new ReturnStruct<SlaveIdData>() return new ReturnStruct<SlaveIdData>()
{ {
Datas = new SlaveIdData() { SlaveId = outputStruct.SlaveId, IndicatorStatus = outputStruct.RunIndicatorStatus, AdditionalData = outputStruct.AdditionalData }, Datas = new SlaveIdData() { SlaveId = outputStruct.SlaveId, IndicatorStatus = outputStruct.RunIndicatorStatus, AdditionalData = outputStruct.AdditionalData },
IsSuccess = true, IsSuccess = outputStruct == null ? null : true,
ErrorCode = 0, ErrorCode = 0,
ErrorMsg = null ErrorMsg = null
}; };
@@ -475,7 +476,7 @@ namespace Modbus.Net.Modbus
return new ReturnStruct<ReadFileRecordOutputDef[]>() return new ReturnStruct<ReadFileRecordOutputDef[]>()
{ {
Datas = outputStruct.RecordDefs, Datas = outputStruct.RecordDefs,
IsSuccess = true, IsSuccess = outputStruct == null ? null : true,
ErrorCode = 0, ErrorCode = 0,
ErrorMsg = null ErrorMsg = null
}; };
@@ -505,7 +506,7 @@ namespace Modbus.Net.Modbus
return new ReturnStruct<WriteFileRecordOutputDef[]>() return new ReturnStruct<WriteFileRecordOutputDef[]>()
{ {
Datas = outputStruct.WriteRecords, Datas = outputStruct.WriteRecords,
IsSuccess = true, IsSuccess = outputStruct == null ? null : true,
ErrorCode = 0, ErrorCode = 0,
ErrorMsg = null ErrorMsg = null
}; };
@@ -535,7 +536,7 @@ namespace Modbus.Net.Modbus
return new ReturnStruct<MaskRegisterData>() return new ReturnStruct<MaskRegisterData>()
{ {
Datas = new MaskRegisterData() { ReferenceAddress = outputStruct.ReferenceAddress, AndMask = outputStruct.AndMask, OrMask = outputStruct.OrMask }, Datas = new MaskRegisterData() { ReferenceAddress = outputStruct.ReferenceAddress, AndMask = outputStruct.AndMask, OrMask = outputStruct.OrMask },
IsSuccess = true, IsSuccess = outputStruct == null ? null : true,
ErrorCode = 0, ErrorCode = 0,
ErrorMsg = null ErrorMsg = null
}; };
@@ -565,7 +566,7 @@ namespace Modbus.Net.Modbus
return new ReturnStruct<ushort[]>() return new ReturnStruct<ushort[]>()
{ {
Datas = outputStruct.ReadRegisterValues, Datas = outputStruct.ReadRegisterValues,
IsSuccess = true, IsSuccess = outputStruct == null ? null : true,
ErrorCode = 0, ErrorCode = 0,
ErrorMsg = null ErrorMsg = null
}; };
@@ -595,7 +596,7 @@ namespace Modbus.Net.Modbus
return new ReturnStruct<ushort[]>() return new ReturnStruct<ushort[]>()
{ {
Datas = outputStruct.FIFOValueRegister, Datas = outputStruct.FIFOValueRegister,
IsSuccess = true, IsSuccess = outputStruct == null ? null : true,
ErrorCode = 0, ErrorCode = 0,
ErrorMsg = null ErrorMsg = null
}; };

View File

@@ -0,0 +1,7 @@
namespace Modbus.Net.Modbus
{
public class ModbusUtilityServer
{
}
}

View File

@@ -1,62 +0,0 @@
using System;
using System.Linq;
namespace Modbus.Net.Opc
{
/// <summary>
/// Opc地址编码器
/// </summary>
public class AddressFormaterOpc<TMachineKey, TUnitKey, TAddressKey, TSubAddressKey> : AddressFormater<TAddressKey, TSubAddressKey> where TMachineKey : IEquatable<TMachineKey>
where TUnitKey : IEquatable<TUnitKey> where TAddressKey : IEquatable<TAddressKey> where TSubAddressKey : IEquatable<TSubAddressKey>
{
/// <summary>
/// 协议构造器
/// </summary>
/// <param name="tagGeter">如何通过BaseMachine和AddressUnit构造Opc的标签</param>
/// <param name="machine">调用这个编码器的设备</param>
public AddressFormaterOpc(Func<BaseMachine<TMachineKey, TUnitKey, TAddressKey, TSubAddressKey>, AddressUnit<TUnitKey, TAddressKey, TSubAddressKey>, string> tagGeter,
BaseMachine<TMachineKey, TUnitKey, TAddressKey, TSubAddressKey> machine)
{
Machine = machine;
TagGeter = tagGeter;
}
/// <summary>
/// 设备
/// </summary>
public BaseMachine<TMachineKey, TUnitKey, TAddressKey, TSubAddressKey> Machine { get; set; }
/// <summary>
/// 标签构造器
/// (设备,地址)->不具备分隔符的标签数组
/// </summary>
protected Func<BaseMachine<TMachineKey, TUnitKey, TAddressKey, TSubAddressKey>, AddressUnit<TUnitKey, TAddressKey, TSubAddressKey>, string> TagGeter { get; set; }
/// <summary>
/// 编码地址
/// </summary>
/// <param name="area">地址所在的数据区域</param>
/// <param name="address">地址</param>
/// <returns>编码后的地址</returns>
public override string FormatAddress(string area, TAddressKey address)
{
var findAddress = Machine?.GetAddresses.FirstOrDefault(p => p.Area == area && p.Address.Equals(address));
if (findAddress == null) return null;
var ans = TagGeter(Machine, findAddress);
ans = ans.Trim().Replace(" ", "");
return ans;
}
/// <summary>
/// 编码地址
/// </summary>
/// <param name="area">地址所在的数据区域</param>
/// <param name="address">地址</param>
/// <param name="subAddress">子地址(忽略)</param>
/// <returns>编码后的地址</returns>
public override string FormatAddress(string area, TAddressKey address, TSubAddressKey subAddress)
{
return FormatAddress(area, address);
}
}
}

View File

@@ -1,31 +0,0 @@
using System;
namespace Modbus.Net.Opc
{
/// <summary>
/// Opc地址解析器
/// </summary>
public class AddressTranslatorOpc : AddressTranslator
{
/// <summary>
/// 地址转换
/// </summary>
/// <param name="address">格式化的地址</param>
/// <param name="isRead">是否为读取,是为读取,否为写入</param>
/// <returns>翻译后的地址</returns>
public override AddressDef AddressTranslate(string address, bool isRead)
{
throw new NotImplementedException();
}
/// <summary>
/// 获取区域中的单个地址占用的字节长度
/// </summary>
/// <param name="area">区域名称</param>
/// <returns>字节长度</returns>
public override double GetAreaByteLength(string area)
{
return 1;
}
}
}

View File

@@ -1,146 +0,0 @@
using Hylasoft.Opc.Common;
using Hylasoft.Opc.Da;
using Hylasoft.Opc.Ua;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Modbus.Net.Opc
{
/// <summary>
/// Opc Client Extend interface, Unified for DA and UA
/// </summary>
public interface IClientExtend : IDisposable
{
/// <summary>
/// Unified Root Node
/// </summary>
Node RootNodeBase { get; }
/// <summary>
/// Connect the client to the Opc Server
/// </summary>
void Connect();
/// <summary>
/// Read a tag
/// </summary>
/// <typeparam name="T">The type of tag to read</typeparam>
/// <param name="tag">
/// The fully-qualified identifier of the tag. You can specify a subfolder by using a comma delimited name.
/// E.g: the tag `foo.bar` reads the tag `bar` on the folder `foo`
/// </param>
/// <returns>The value retrieved from the Opc</returns>
ReadEvent<T> Read<T>(string tag);
/// <summary>
/// Write a value on the specified Opc tag
/// </summary>
/// <typeparam name="T">The type of tag to write on</typeparam>
/// <param name="tag">
/// The fully-qualified identifier of the tag. You can specify a subfolder by using a comma delimited name.
/// E.g: the tag `foo.bar` writes on the tag `bar` on the folder `foo`
/// </param>
/// <param name="item"></param>
void Write<T>(string tag, T item);
/// <summary>
/// Read a tag asynchronusly
/// </summary>
Task<ReadEvent<T>> ReadAsync<T>(string tag);
/// <summary>
/// Write a value on the specified Opc tag asynchronously
/// </summary>
Task WriteAsync<T>(string tag, T item);
/// <summary>
/// Finds a node on the Opc Server asynchronously
/// </summary>
Task<Node> FindNodeAsync(string tag);
/// <summary>
/// Explore a folder on the Opc Server asynchronously
/// </summary>
Task<IEnumerable<Node>> ExploreFolderAsync(string tag);
}
/// <summary>
/// UaClient Extend
/// </summary>
public class MyDaClient : DaClient, IClientExtend
{
/// <summary>
/// 构造函数
/// </summary>
/// <param name="serverUrl">OpcDa服务端Url</param>
public MyDaClient(Uri serverUrl) : base(serverUrl)
{
}
/// <summary>
/// Unified root node
/// </summary>
public Node RootNodeBase => RootNode;
}
/// <summary>
/// DaClient Extend
/// </summary>
public class MyUaClient : UaClient, IClientExtend
{
/// <summary>
/// DaClient Extend
/// </summary>
public MyUaClient(Uri serverUrl) : base(serverUrl)
{
}
/// <summary>
/// Unified root node
/// </summary>
public Node RootNodeBase => RootNode;
}
/// <summary>
/// Param input of OpcConnector
/// </summary>
public class OpcParamIn
{
/// <summary>
/// Is the action read (not is write)
/// </summary>
public bool IsRead { get; set; }
/// <summary>
/// Tag of a node
/// </summary>
public string Tag { get; set; }
/// <summary>
/// Tag splitter of a node
/// </summary>
public char Split { get; set; }
/// <summary>
/// The value set to node(only available when IsRead is false
/// </summary>
public object SetValue { get; set; }
}
/// <summary>
/// Param output of OpcConnector
/// </summary>
public class OpcParamOut
{
/// <summary>
/// Is the action success
/// </summary>
public bool Success { get; set; }
/// <summary>
/// Action return values
/// </summary>
public byte[] Value { get; set; }
}
}

View File

@@ -1,41 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>10.0</LangVersion>
<AssemblyName>Modbus.Net.Opc</AssemblyName>
<RootNamespace>Modbus.Net.Opc</RootNamespace>
<PackageId>Modbus.Net.Opc</PackageId>
<Version>1.4.3</Version>
<Authors>Chris L.(Luo Sheng)</Authors>
<Company>Hangzhou Delian Science Technology Co.,Ltd.</Company>
<Product>Modbus.Net.Opc</Product>
<Description>Modbus.Net Opc Implementation</Description>
<Copyright>Copyright 2023 Hangzhou Delian Science Technology Co.,Ltd.</Copyright>
<PackageProjectUrl>https://github.com/parallelbgls/Modbus.Net/tree/master/Modbus.Net/Modbus.Net.Opc</PackageProjectUrl>
<RepositoryUrl>https://github.com/parallelbgls/Modbus.Net</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>hardware communicate protocol modbus Delian</PackageTags>
<PackageRequireLicenseAcceptance>False</PackageRequireLicenseAcceptance>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<IncludeSymbols>True</IncludeSymbols>
<IncludeSource>True</IncludeSource>
<PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
<PackageReadmeFile>README.md</PackageReadmeFile>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<Platforms>AnyCPU</Platforms>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DocumentationFile>bin\Debug\Modbus.Net.Opc.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\h-opc\h-opc\h-opc.csproj" />
<ProjectReference Include="..\Modbus.Net\Modbus.Net.csproj" />
</ItemGroup>
<ItemGroup>
<None Include="README.md" Pack="true" PackagePath="" />
</ItemGroup>
</Project>

View File

@@ -1,204 +0,0 @@
using Microsoft.Extensions.Logging;
using System;
using System.Text;
using System.Threading.Tasks;
namespace Modbus.Net.Opc
{
/// <summary>
/// Opc连接器
/// </summary>
public abstract class OpcConnector : BaseConnector<OpcParamIn, OpcParamOut>
{
private static readonly ILogger<OpcConnector> logger = LogProvider.CreateLogger<OpcConnector>();
/// <summary>
/// 是否正在连接
/// </summary>
protected bool _connect;
/// <summary>
/// Opc客户端
/// </summary>
protected IClientExtend Client;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="host">服务端url</param>
protected OpcConnector(string host)
{
ConnectionToken = host;
}
/// <summary>
/// 连接标识
/// </summary>
public override string ConnectionToken { get; }
/// <summary>
/// 是否正在连接
/// </summary>
public override bool IsConnected => _connect;
/// <summary>
/// 断开连接
/// </summary>
/// <returns></returns>
public override bool Disconnect()
{
try
{
Client?.Dispose();
Client = null;
_connect = false;
logger.LogInformation("Opc client {ConnectionToken} disconnected success", ConnectionToken);
return true;
}
catch (Exception ex)
{
logger.LogError(ex, "Opc client {ConnectionToken} disconnected error", ConnectionToken);
_connect = false;
return false;
}
}
/// <inheritdoc />
protected override void ReceiveMsgThreadStart()
{
throw new NotImplementedException();
}
/// <inheritdoc />
protected override void ReceiveMsgThreadStop()
{
throw new NotImplementedException();
}
/// <inheritdoc />
protected override Task SendMsgWithoutConfirm(OpcParamIn message)
{
throw new NotImplementedException();
}
/// <summary>
/// 带返回发送数据
/// </summary>
/// <param name="message">需要发送的数据</param>
/// <returns>是否发送成功</returns>
public override async Task<OpcParamOut> SendMsgAsync(OpcParamIn message)
{
try
{
if (message.IsRead)
{
var tag = message.Tag;
if (tag != null)
{
var result = await Client.ReadAsync<object>(tag);
object resultTrans;
if (result.Value?.ToString() == "False")
{
resultTrans = (byte)0;
}
else if (result.Value?.ToString() == "True")
{
resultTrans = (byte)1;
}
else if (result.Value != null)
{
resultTrans = result.Value;
}
else
{
logger.LogError($"Opc Machine {ConnectionToken} Read Opc tag {tag} for value null");
return new OpcParamOut
{
Success = false,
Value = Encoding.ASCII.GetBytes("NoData")
};
}
logger.LogInformation($"Opc Machine {ConnectionToken} Read Opc tag {tag} for value {result.Value} {result.Value.GetType().FullName}");
return new OpcParamOut
{
Success = true,
Value = BigEndianLsbValueHelper.Instance.GetBytes(resultTrans, resultTrans.GetType())
};
}
logger.LogError($"Opc Machine {ConnectionToken} Read Opc tag null");
return new OpcParamOut
{
Success = false,
Value = Encoding.ASCII.GetBytes("NoData")
};
}
else
{
var tag = message.Tag;
var value = message.SetValue;
;
if (tag != null)
{
try
{
await Client.WriteAsync(tag, value);
logger.LogInformation($"Opc Machine {ConnectionToken} Write Opc tag {tag} for value {value}");
}
catch (Exception e)
{
logger.LogError(e, "Opc client {ConnectionToken} write exception", ConnectionToken);
return new OpcParamOut
{
Success = false
};
}
return new OpcParamOut
{
Success = true
};
}
return new OpcParamOut
{
Success = false
};
}
}
catch (Exception e)
{
logger.LogError(e, "Opc client {ConnectionToken} read exception", ConnectionToken);
Disconnect();
return new OpcParamOut
{
Success = false,
Value = Encoding.ASCII.GetBytes("NoData")
};
}
}
private bool Connect()
{
try
{
Client.Connect();
_connect = true;
logger.LogInformation("Opc client {ConnectionToken} connect success", ConnectionToken);
return true;
}
catch (Exception ex)
{
logger.LogError(ex, "Opc client {ConnectionToken} connected failed", ConnectionToken);
_connect = false;
return false;
}
}
/// <summary>
/// 连接PLC异步
/// </summary>
/// <returns>是否连接成功</returns>
public override Task<bool> ConnectAsync()
{
return Task.FromResult(Connect());
}
}
}

View File

@@ -1,47 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Modbus.Net.Opc
{
/// <summary>
/// Opc DA连接实现
/// </summary>
public class OpcDaConnector : OpcConnector
{
/// <summary>
/// DA单例管理
/// </summary>
protected static Dictionary<string, OpcDaConnector> _instances = new Dictionary<string, OpcDaConnector>();
/// <summary>
/// 构造函数
/// </summary>
/// <param name="host">Opc DA 服务地址</param>
protected OpcDaConnector(string host) : base(host)
{
}
/// <summary>
/// 根据服务地址生成DA单例
/// </summary>
/// <param name="host">Opc DA 服务地址</param>
/// <returns>Opc DA 连接器实例</returns>
public static OpcDaConnector Instance(string host)
{
if (!_instances.ContainsKey(host))
{
var connector = new OpcDaConnector(host);
_instances.Add(host, connector);
}
return _instances[host];
}
/// <inheritdoc />
public override Task<bool> ConnectAsync()
{
if (Client == null) Client = new MyDaClient(new Uri(ConnectionToken));
return base.ConnectAsync();
}
}
}

View File

@@ -1,34 +0,0 @@
using System.Threading.Tasks;
namespace Modbus.Net.Opc
{
/// <summary>
/// Opc Da协议
/// </summary>
public class OpcDaProtocol : OpcProtocol
{
private readonly string _host;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="host">Opc DA服务地址</param>
public OpcDaProtocol(string host)
{
_host = host;
}
/// <summary>
/// 连接设备
/// </summary>
/// <returns>是否连接成功</returns>
public override async Task<bool> ConnectAsync()
{
ProtocolLinker = new OpcDaProtocolLinker(_host);
if (!await ProtocolLinker.ConnectAsync())
return false;
return true;
}
}
}

View File

@@ -1,24 +0,0 @@
namespace Modbus.Net.Opc
{
/// <summary>
/// Opc Da协议连接器
/// </summary>
public class OpcDaProtocolLinker : OpcProtocolLinker
{
/// <summary>
/// 构造函数
/// </summary>
public OpcDaProtocolLinker() : this(ConfigurationReader.GetValueDirect("OpcDa", "Host"))
{
}
/// <summary>
/// 构造函数
/// </summary>
/// <param name="host">Opc DA服务地址</param>
public OpcDaProtocolLinker(string host)
{
BaseConnector = OpcDaConnector.Instance(host);
}
}
}

View File

@@ -1,27 +0,0 @@
using System;
using System.Collections.Generic;
namespace Modbus.Net.Opc
{
/// <summary>
/// Opc设备
/// </summary>
public class OpcMachine<TKey, TUnitKey> : BaseMachine<TKey, TUnitKey, string, string> where TKey : IEquatable<TKey>
where TUnitKey : IEquatable<TUnitKey>
{
/// <summary>
/// 构造函数
/// </summary>
/// <param name="id">设备的ID号</param>
/// <param name="connectionType">连接类型</param>
/// <param name="connectionString">连接地址</param>
/// <param name="getAddresses">需要读写的地址</param>
/// <param name="tagSpliter">连接各个字段用的符号</param>
public OpcMachine(TKey id, OpcType connectionType, string connectionString, IEnumerable<AddressUnit<TUnitKey, string, string>> getAddresses, string tagSpliter)
: base(id, getAddresses, true)
{
BaseUtility = new OpcUtility(connectionType, connectionString);
AddressFormater = new AddressFormaterOpc<TKey, TUnitKey, string, string>((machine, unit) => { var ans = unit.Area; if (unit.Address != null) ans += tagSpliter + unit.Address; if (unit.SubAddress != null) ans += tagSpliter + unit.SubAddress; return ans; }, this);
}
}
}

View File

@@ -1,181 +0,0 @@
namespace Modbus.Net.Opc
{
/// <summary>
/// Opc协议
/// </summary>
public abstract class OpcProtocol : BaseProtocol<OpcParamIn, OpcParamOut, ProtocolUnit<OpcParamIn, OpcParamOut>,
PipeUnit<OpcParamIn, OpcParamOut, IProtocolLinker<OpcParamIn, OpcParamOut>,
ProtocolUnit<OpcParamIn, OpcParamOut>>>
{
/// <summary>
/// 构造函数
/// </summary>
protected OpcProtocol() : base(0, 0, Endian.BigEndianLsb)
{
}
}
#region
/// <summary>
/// 读数据输入
/// </summary>
public class ReadRequestOpcInputStruct : IInputStruct
{
/// <summary>
/// 构造函数
/// </summary>
/// <param name="tag">标签</param>
public ReadRequestOpcInputStruct(string tag)
{
Tag = tag;
}
/// <summary>
/// 标签
/// </summary>
public string Tag { get; }
}
/// <summary>
/// 读地址输出
/// </summary>
public class ReadRequestOpcOutputStruct : IOutputStruct
{
/// <summary>
/// 构造函数
/// </summary>
/// <param name="value">读取的数据</param>
public ReadRequestOpcOutputStruct(byte[] value)
{
GetValue = value;
}
/// <summary>
/// 读取的地址
/// </summary>
public byte[] GetValue { get; private set; }
}
/// <summary>
/// 读数据协议
/// </summary>
[SpecialProtocolUnit]
public class ReadRequestOpcProtocol : ProtocolUnit<OpcParamIn, OpcParamOut>
{
/// <summary>
/// 从对象的参数数组格式化
/// </summary>
/// <param name="message">非结构化的输入数据</param>
/// <returns>格式化后的字节流</returns>
public override OpcParamIn Format(IInputStruct message)
{
var r_message = (ReadRequestOpcInputStruct)message;
return new OpcParamIn
{
IsRead = true,
Tag = r_message.Tag,
};
}
/// <summary>
/// 把仪器返回的内容填充到输出结构中
/// </summary>
/// <param name="messageBytes">返回数据的字节流</param>
/// <param name="pos">转换标记位</param>
/// <returns>结构化的输出数据</returns>
public override IOutputStruct Unformat(OpcParamOut messageBytes, ref int pos)
{
return new ReadRequestOpcOutputStruct(messageBytes.Value);
}
}
#endregion
#region
/// <summary>
/// 写数据输入
/// </summary>
public class WriteRequestOpcInputStruct : IInputStruct
{
/// <summary>
/// 构造函数
/// </summary>
/// <param name="tag">标签</param>
/// <param name="setValue">写入的数据</param>
public WriteRequestOpcInputStruct(string tag, object setValue)
{
Tag = tag;
SetValue = setValue;
}
/// <summary>
/// 标签
/// </summary>
public string Tag { get; }
/// <summary>
/// 写入的数据
/// </summary>
public object SetValue { get; }
}
/// <summary>
/// 写数据输出
/// </summary>
public class WriteRequestOpcOutputStruct : IOutputStruct
{
/// <summary>
/// 构造函数
/// </summary>
/// <param name="writeResult">写入是否成功</param>
public WriteRequestOpcOutputStruct(bool writeResult)
{
WriteResult = writeResult;
}
/// <summary>
/// 写入是否成功
/// </summary>
public bool WriteResult { get; private set; }
}
/// <summary>
/// 写数据协议
/// </summary>
[SpecialProtocolUnit]
public class WriteRequestOpcProtocol : ProtocolUnit<OpcParamIn, OpcParamOut>
{
/// <summary>
/// 从对象的参数数组格式化
/// </summary>
/// <param name="message">非结构化的输入数据</param>
/// <returns>格式化后的字节流</returns>
public override OpcParamIn Format(IInputStruct message)
{
var r_message = (WriteRequestOpcInputStruct)message;
return new OpcParamIn
{
IsRead = false,
Tag = r_message.Tag,
SetValue = r_message.SetValue
};
}
/// <summary>
/// 把仪器返回的内容填充到输出结构中
/// </summary>
/// <param name="messageBytes">返回数据的字节流</param>
/// <param name="pos">转换标记位</param>
/// <returns>结构化的输出数据</returns>
public override IOutputStruct Unformat(OpcParamOut messageBytes, ref int pos)
{
var ansByte = BigEndianLsbValueHelper.Instance.GetByte(messageBytes.Value, ref pos);
var ans = ansByte != 0;
return new WriteRequestOpcOutputStruct(ans);
}
}
#endregion
}

View File

@@ -1,42 +0,0 @@
using System.Text;
using System.Threading.Tasks;
namespace Modbus.Net.Opc
{
/// <summary>
/// Opc协议连接器
/// </summary>
public abstract class OpcProtocolLinker : ProtocolLinker<OpcParamIn, OpcParamOut>
{
/// <summary>
/// 发送并接收数据,不进行协议扩展和收缩,用于特殊协议
/// </summary>
/// <param name="content">发送协议的内容</param>
/// <returns>接收协议的内容</returns>
public override async Task<OpcParamOut> SendReceiveWithoutExtAndDecAsync(OpcParamIn content)
{
//发送数据
var receiveBytes = await BaseConnector.SendMsgAsync(content);
//容错处理
var checkRight = CheckRight(receiveBytes);
return checkRight == null
? new OpcParamOut { Success = false, Value = new byte[0] }
: (!checkRight.Value ? null : receiveBytes);
//返回字符
}
/// <summary>
/// 检查接收的数据是否正确
/// </summary>
/// <param name="content">接收协议的内容</param>
/// <returns>协议是否是正确的</returns>
public override bool? CheckRight(OpcParamOut content)
{
if (content == null || !content.Success) return false;
if (content.Success && content.Value == null) { content.Value = Encoding.ASCII.GetBytes("Success"); return true; }
if (content.Value.Length == 6 && Encoding.ASCII.GetString(content.Value) == "NoData")
return null;
return base.CheckRight(content);
}
}
}

View File

@@ -1,47 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Modbus.Net.Opc
{
/// <summary>
/// Opc UA连接实现
/// </summary>
public class OpcUaConnector : OpcConnector
{
/// <summary>
/// UA单例管理
/// </summary>
protected static Dictionary<string, OpcUaConnector> _instances = new Dictionary<string, OpcUaConnector>();
/// <summary>
/// 构造函数
/// </summary>
/// <param name="host">Opc UA 服务地址</param>
protected OpcUaConnector(string host) : base(host)
{
}
/// <summary>
/// 根据地址获取UA连接器单例
/// </summary>
/// <param name="host">Opc UA服务地址</param>
/// <returns>Opc UA实例</returns>
public static OpcUaConnector Instance(string host)
{
if (!_instances.ContainsKey(host))
{
var connector = new OpcUaConnector(host);
_instances.Add(host, connector);
}
return _instances[host];
}
/// <inheritdoc />
public override Task<bool> ConnectAsync()
{
if (Client == null) Client = new MyUaClient(new Uri(ConnectionToken));
return base.ConnectAsync();
}
}
}

View File

@@ -1,32 +0,0 @@
using System.Threading.Tasks;
namespace Modbus.Net.Opc
{
/// <summary>
/// Opc UA协议
/// </summary>
public class OpcUaProtocol : OpcProtocol
{
private readonly string _host;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="host">Opc UA服务地址</param>
public OpcUaProtocol(string host)
{
_host = host;
}
/// <summary>
/// 连接设备
/// </summary>
/// <returns>是否连接成功</returns>
public override async Task<bool> ConnectAsync()
{
ProtocolLinker = new OpcUaProtocolLinker(_host);
if (!await ProtocolLinker.ConnectAsync()) return false;
return true;
}
}
}

View File

@@ -1,24 +0,0 @@
namespace Modbus.Net.Opc
{
/// <summary>
/// Opc UA协议连接器
/// </summary>
public class OpcUaProtocolLinker : OpcProtocolLinker
{
/// <summary>
/// 构造函数
/// </summary>
public OpcUaProtocolLinker() : this(ConfigurationReader.GetValueDirect("OpcUa", "Host"))
{
}
/// <summary>
/// 构造函数
/// </summary>
/// <param name="host">Opc UA服务地址</param>
public OpcUaProtocolLinker(string host)
{
BaseConnector = OpcUaConnector.Instance(host);
}
}
}

View File

@@ -1,171 +0,0 @@
using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;
namespace Modbus.Net.Opc
{
/// <summary>
/// Opc类型
/// </summary>
public enum OpcType
{
/// <summary>
/// DA连接
/// </summary>
Da = 0,
/// <summary>
/// UA连接
/// </summary>
Ua = 1
}
/// <summary>
/// Opc通用Api入口
/// </summary>
public class OpcUtility : BaseUtility<OpcParamIn, OpcParamOut, ProtocolUnit<OpcParamIn, OpcParamOut>,
PipeUnit<OpcParamIn, OpcParamOut, IProtocolLinker<OpcParamIn, OpcParamOut>,
ProtocolUnit<OpcParamIn, OpcParamOut>>>
{
private static readonly ILogger<OpcUtility> logger = LogProvider.CreateLogger<OpcUtility>();
private OpcType _opcType;
/// <summary>
/// 协议类型
/// </summary>
public OpcType OpcType
{
get { return _opcType; }
set
{
_opcType = value;
switch (_opcType)
{
//Da协议
case OpcType.Da:
{
Wrapper = new OpcDaProtocol(ConnectionString);
break;
}
//Ua协议
case OpcType.Ua:
{
Wrapper = new OpcUaProtocol(ConnectionString);
break;
}
}
}
}
/// <summary>
/// 构造函数
/// </summary>
/// <param name="connectionType">连接类型</param>
/// <param name="connectionString">连接地址</param>
public OpcUtility(int connectionType, string connectionString) : base(0, 0)
{
ConnectionString = connectionString;
OpcType = (OpcType)connectionType;
AddressTranslator = new AddressTranslatorOpc();
}
/// <summary>
/// 构造函数
/// </summary>
/// <param name="connectionType">连接类型</param>
/// <param name="connectionString">连接地址</param>
public OpcUtility(OpcType connectionType, string connectionString) : base(0, 0)
{
ConnectionString = connectionString;
OpcType = connectionType;
AddressTranslator = new AddressTranslatorOpc();
}
/// <summary>
/// 端格式(大端)
/// </summary>
public override Endian Endian => Endian.BigEndianLsb;
/// <summary>
/// 设置连接方式(Opc忽略该函数)
/// </summary>
/// <param name="connectionType">连接方式</param>
public override void SetConnectionType(int connectionType)
{
//ignore
}
/// <summary>
/// 获取数据
/// </summary>
/// <param name="startAddress">开始地址</param>
/// <param name="getByteCount">获取字节数个数</param>
/// <returns>接收到的byte数据</returns>
public override async Task<ReturnStruct<byte[]>> GetDatasAsync(string startAddress, int getByteCount)
{
try
{
var readRequestOpcInputStruct = new ReadRequestOpcInputStruct(startAddress);
var readRequestOpcOutputStruct =
await
Wrapper.SendReceiveAsync<ReadRequestOpcOutputStruct>(Wrapper[typeof(ReadRequestOpcProtocol)],
readRequestOpcInputStruct);
return new ReturnStruct<byte[]>
{
Datas = readRequestOpcOutputStruct?.GetValue,
IsSuccess = true,
ErrorCode = 0,
ErrorMsg = ""
};
}
catch (Exception e)
{
logger.LogError(e, $"OpcUtility -> GetDatas: {ConnectionString} error: {e.Message}");
return new ReturnStruct<byte[]>
{
Datas = null,
IsSuccess = true,
ErrorCode = -100,
ErrorMsg = e.Message
};
}
}
/// <summary>
/// 设置数据
/// </summary>
/// <param name="startAddress">开始地址</param>
/// <param name="setContents">设置数据</param>
/// <returns>是否设置成功</returns>
public override async Task<ReturnStruct<bool>> SetDatasAsync(string startAddress, object[] setContents)
{
try
{
var writeRequestOpcInputStruct =
new WriteRequestOpcInputStruct(startAddress, setContents[0]);
var writeRequestOpcOutputStruct =
await
Wrapper.SendReceiveAsync<WriteRequestOpcOutputStruct>(Wrapper[typeof(WriteRequestOpcProtocol)],
writeRequestOpcInputStruct);
return new ReturnStruct<bool>
{
Datas = writeRequestOpcOutputStruct?.WriteResult == true,
IsSuccess = writeRequestOpcOutputStruct?.WriteResult == true,
ErrorCode = writeRequestOpcOutputStruct?.WriteResult == true ? 0 : 1,
ErrorMsg = writeRequestOpcOutputStruct?.WriteResult == true ? "" : "Write Failed"
};
}
catch (Exception e)
{
logger.LogError(e, $"OpcUtility -> SetDatas: {ConnectionString} error: {e.Message}");
return new ReturnStruct<bool>
{
Datas = false,
IsSuccess = false,
ErrorCode = -100,
ErrorMsg = e.Message
};
}
}
}
}

View File

@@ -1,12 +0,0 @@
Modbus.Net.Opc
===================
[![NuGet](https://img.shields.io/nuget/v/Modbus.Net.Opc.svg)](https://www.nuget.org/packages/Modbus.Net.Opc/)
OPC Implementation of Modbus.Net
Doc has been moved to wiki.
## Caution
Do not use this module in commercial environment.<br>
Altered h-opc uses Technosoftware.DaAeHdaSolution that will not abey for non paid commercial use.

View File

@@ -22,9 +22,9 @@ namespace Modbus.Net.Siemens
/// <param name="masterAddress">主站号</param> /// <param name="masterAddress">主站号</param>
/// <param name="src">本机模块位0到7仅200使用其它型号不要填写</param> /// <param name="src">本机模块位0到7仅200使用其它型号不要填写</param>
/// <param name="dst">PLC模块位0到7仅200使用其它型号不要填写</param> /// <param name="dst">PLC模块位0到7仅200使用其它型号不要填写</param>
public SiemensMachine(TKey id, SiemensType connectionType, string connectionString, SiemensMachineModel model, public SiemensMachine(TKey id, string alias, SiemensType connectionType, string connectionString, SiemensMachineModel model,
IEnumerable<AddressUnit<TUnitKey, int, int>> getAddresses, bool keepConnect, byte slaveAddress, byte masterAddress, byte src = 1, byte dst = 0) IEnumerable<AddressUnit<TUnitKey, int, int>> getAddresses, bool keepConnect, byte slaveAddress, byte masterAddress, byte src = 1, byte dst = 0)
: base(id, getAddresses, keepConnect, slaveAddress, masterAddress) : base(id, alias, getAddresses, keepConnect, slaveAddress, masterAddress)
{ {
BaseUtility = new SiemensUtility(connectionType, connectionString, model, slaveAddress, masterAddress, src, dst); BaseUtility = new SiemensUtility(connectionType, connectionString, model, slaveAddress, masterAddress, src, dst);
AddressFormater = new AddressFormaterSiemens(); AddressFormater = new AddressFormaterSiemens();
@@ -44,9 +44,9 @@ namespace Modbus.Net.Siemens
/// <param name="masterAddress">主站号</param> /// <param name="masterAddress">主站号</param>
/// <param name="src">本机模块位0到7仅200使用其它型号不要填写</param> /// <param name="src">本机模块位0到7仅200使用其它型号不要填写</param>
/// <param name="dst">PLC模块位0到7仅200使用其它型号不要填写</param> /// <param name="dst">PLC模块位0到7仅200使用其它型号不要填写</param>
public SiemensMachine(TKey id, SiemensType connectionType, string connectionString, SiemensMachineModel model, public SiemensMachine(TKey id, string alias, SiemensType connectionType, string connectionString, SiemensMachineModel model,
IEnumerable<AddressUnit<TUnitKey, int, int>> getAddresses, byte slaveAddress, byte masterAddress, byte src = 1, byte dst = 0) IEnumerable<AddressUnit<TUnitKey, int, int>> getAddresses, byte slaveAddress, byte masterAddress, byte src = 1, byte dst = 0)
: this(id, connectionType, connectionString, model, getAddresses, true, slaveAddress, masterAddress, src, dst) : this(id, alias, connectionType, connectionString, model, getAddresses, true, slaveAddress, masterAddress, src, dst)
{ {
} }
} }

View File

@@ -237,8 +237,9 @@ namespace Modbus.Net.Siemens
/// </summary> /// </summary>
/// <param name="startAddress">开始地址</param> /// <param name="startAddress">开始地址</param>
/// <param name="getByteCount">读取字节个数</param> /// <param name="getByteCount">读取字节个数</param>
/// <param name="getOriginalCount">读取原始个数</param>
/// <returns>从设备中读取的数据</returns> /// <returns>从设备中读取的数据</returns>
public override async Task<ReturnStruct<byte[]>> GetDatasAsync(string startAddress, int getByteCount) public override async Task<ReturnStruct<byte[]>> GetDatasAsync(string startAddress, int getByteCount, int getOriginalCount)
{ {
try try
{ {
@@ -291,8 +292,9 @@ namespace Modbus.Net.Siemens
/// </summary> /// </summary>
/// <param name="startAddress">开始地址</param> /// <param name="startAddress">开始地址</param>
/// <param name="setContents">需要写入的数据</param> /// <param name="setContents">需要写入的数据</param>
/// <param name="setOriginalCount">写入数据的原始长度</param>
/// <returns>写入是否成功</returns> /// <returns>写入是否成功</returns>
public override async Task<ReturnStruct<bool>> SetDatasAsync(string startAddress, object[] setContents) public override async Task<ReturnStruct<bool>> SetDatasAsync(string startAddress, object[] setContents, int setOriginalCount)
{ {
try try
{ {

View File

@@ -27,29 +27,23 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Modbus.Net.Modbus.NA200H",
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AnyType", "..\Samples\AnyType\AnyType.csproj", "{1857DA63-3335-428F-84D8-1FA4F8178643}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AnyType", "..\Samples\AnyType\AnyType.csproj", "{1857DA63-3335-428F-84D8-1FA4F8178643}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Modbus.Net.Opc", "Modbus.Net.OPC\Modbus.Net.Opc.csproj", "{C854A379-C5EA-4CAC-9C5F-7291372D1D3F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CrossLamp", "..\Samples\CrossLamp\CrossLamp.csproj", "{AA3A42D2-0502-41D3-929A-BAB729DF07D6}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CrossLamp", "..\Samples\CrossLamp\CrossLamp.csproj", "{AA3A42D2-0502-41D3-929A-BAB729DF07D6}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TripleAdd", "..\Samples\TripleAdd\TripleAdd.csproj", "{414956B8-DBD4-414C-ABD3-565580739646}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TripleAdd", "..\Samples\TripleAdd\TripleAdd.csproj", "{414956B8-DBD4-414C-ABD3-565580739646}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Modbus.Net.Modbus.SelfDefinedSample", "Modbus.Net.Modbus.SelfDefinedSample\Modbus.Net.Modbus.SelfDefinedSample.csproj", "{C4FA55AF-80ED-4467-948F-8EF865C8A5A5}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Modbus.Net.Modbus.SelfDefinedSample", "Modbus.Net.Modbus.SelfDefinedSample\Modbus.Net.Modbus.SelfDefinedSample.csproj", "{C4FA55AF-80ED-4467-948F-8EF865C8A5A5}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "h-opc", "..\h-opc\h-opc\h-opc.csproj", "{DC6425E4-1409-488D-A014-4DCC909CF542}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Modbus.Net.BigEndian3412", "Modbus.Net.BigEndian3412\Modbus.Net.BigEndian3412.csproj", "{D48D4F79-1DA2-4C91-A9EE-FDCAEC09E808}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Modbus.Net.BigEndian3412", "Modbus.Net.BigEndian3412\Modbus.Net.BigEndian3412.csproj", "{D48D4F79-1DA2-4C91-A9EE-FDCAEC09E808}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Technosoftware.OpcRcw", "..\Technosoftware\OpcRcw\Technosoftware.OpcRcw.csproj", "{7689CBF8-1992-467D-AD45-E1464F705220}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Technosoftware.DaAeHdaClient", "..\Technosoftware\DaAeHdaClient\Technosoftware.DaAeHdaClient.csproj", "{116160B2-7D6D-40A2-839C-7997BC0E1A0C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Technosoftware.DaAeHdaClient.Com", "..\Technosoftware\DaAeHdaClient.Com\Technosoftware.DaAeHdaClient.Com.csproj", "{ACAF0A16-FC51-4369-BFA8-484FF20707D7}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Modbus.Net.HJ212", "Modbus.Net.HJ212\Modbus.Net.HJ212.csproj", "{057644EF-1407-4C2B-808A-AEF0F2979EA8}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Modbus.Net.HJ212", "Modbus.Net.HJ212\Modbus.Net.HJ212.csproj", "{057644EF-1407-4C2B-808A-AEF0F2979EA8}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Modbus.Net.CodeGenerator", "Modbus.Net.CodeGenerator\Modbus.Net.CodeGenerator.csproj", "{D3210531-BA79-49B2-9F99-09FD0E1627B6}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Modbus.Net.CodeGenerator", "Modbus.Net.CodeGenerator\Modbus.Net.CodeGenerator.csproj", "{D3210531-BA79-49B2-9F99-09FD0E1627B6}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MachineJob.CodeGenerator", "..\Samples\MachineJob.CodeGenerator\MachineJob.CodeGenerator.csproj", "{7337BC9A-ED07-463D-8FCD-A82896CEC6BE}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MachineJob.CodeGenerator", "..\Samples\MachineJob.CodeGenerator\MachineJob.CodeGenerator.csproj", "{7337BC9A-ED07-463D-8FCD-A82896CEC6BE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ModbusTcpToRtu", "..\Samples\ModbusTcpToRtu\ModbusTcpToRtu.csproj", "{9CA7E35C-B5BC-4743-8732-1D8912AA12D8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleModbusRtuServer", "..\Samples\SampleModbusRtuServer\SampleModbusRtuServer.csproj", "{CB85D4B3-EEA2-431F-A47F-1DE7FB0A4802}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -115,14 +109,6 @@ Global
{1857DA63-3335-428F-84D8-1FA4F8178643}.Release|Any CPU.Build.0 = Release|Any CPU {1857DA63-3335-428F-84D8-1FA4F8178643}.Release|Any CPU.Build.0 = Release|Any CPU
{1857DA63-3335-428F-84D8-1FA4F8178643}.Release|x64.ActiveCfg = Release|Any CPU {1857DA63-3335-428F-84D8-1FA4F8178643}.Release|x64.ActiveCfg = Release|Any CPU
{1857DA63-3335-428F-84D8-1FA4F8178643}.Release|x64.Build.0 = Release|Any CPU {1857DA63-3335-428F-84D8-1FA4F8178643}.Release|x64.Build.0 = Release|Any CPU
{C854A379-C5EA-4CAC-9C5F-7291372D1D3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C854A379-C5EA-4CAC-9C5F-7291372D1D3F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C854A379-C5EA-4CAC-9C5F-7291372D1D3F}.Debug|x64.ActiveCfg = Debug|Any CPU
{C854A379-C5EA-4CAC-9C5F-7291372D1D3F}.Debug|x64.Build.0 = Debug|Any CPU
{C854A379-C5EA-4CAC-9C5F-7291372D1D3F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C854A379-C5EA-4CAC-9C5F-7291372D1D3F}.Release|Any CPU.Build.0 = Release|Any CPU
{C854A379-C5EA-4CAC-9C5F-7291372D1D3F}.Release|x64.ActiveCfg = Release|Any CPU
{C854A379-C5EA-4CAC-9C5F-7291372D1D3F}.Release|x64.Build.0 = Release|Any CPU
{AA3A42D2-0502-41D3-929A-BAB729DF07D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AA3A42D2-0502-41D3-929A-BAB729DF07D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AA3A42D2-0502-41D3-929A-BAB729DF07D6}.Debug|Any CPU.Build.0 = Debug|Any CPU {AA3A42D2-0502-41D3-929A-BAB729DF07D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AA3A42D2-0502-41D3-929A-BAB729DF07D6}.Debug|x64.ActiveCfg = Debug|Any CPU {AA3A42D2-0502-41D3-929A-BAB729DF07D6}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -147,14 +133,6 @@ Global
{C4FA55AF-80ED-4467-948F-8EF865C8A5A5}.Release|Any CPU.Build.0 = Release|Any CPU {C4FA55AF-80ED-4467-948F-8EF865C8A5A5}.Release|Any CPU.Build.0 = Release|Any CPU
{C4FA55AF-80ED-4467-948F-8EF865C8A5A5}.Release|x64.ActiveCfg = Release|Any CPU {C4FA55AF-80ED-4467-948F-8EF865C8A5A5}.Release|x64.ActiveCfg = Release|Any CPU
{C4FA55AF-80ED-4467-948F-8EF865C8A5A5}.Release|x64.Build.0 = Release|Any CPU {C4FA55AF-80ED-4467-948F-8EF865C8A5A5}.Release|x64.Build.0 = Release|Any CPU
{DC6425E4-1409-488D-A014-4DCC909CF542}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DC6425E4-1409-488D-A014-4DCC909CF542}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DC6425E4-1409-488D-A014-4DCC909CF542}.Debug|x64.ActiveCfg = Debug|Any CPU
{DC6425E4-1409-488D-A014-4DCC909CF542}.Debug|x64.Build.0 = Debug|Any CPU
{DC6425E4-1409-488D-A014-4DCC909CF542}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DC6425E4-1409-488D-A014-4DCC909CF542}.Release|Any CPU.Build.0 = Release|Any CPU
{DC6425E4-1409-488D-A014-4DCC909CF542}.Release|x64.ActiveCfg = Release|Any CPU
{DC6425E4-1409-488D-A014-4DCC909CF542}.Release|x64.Build.0 = Release|Any CPU
{D48D4F79-1DA2-4C91-A9EE-FDCAEC09E808}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D48D4F79-1DA2-4C91-A9EE-FDCAEC09E808}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D48D4F79-1DA2-4C91-A9EE-FDCAEC09E808}.Debug|Any CPU.Build.0 = Debug|Any CPU {D48D4F79-1DA2-4C91-A9EE-FDCAEC09E808}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D48D4F79-1DA2-4C91-A9EE-FDCAEC09E808}.Debug|x64.ActiveCfg = Debug|Any CPU {D48D4F79-1DA2-4C91-A9EE-FDCAEC09E808}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -163,30 +141,6 @@ Global
{D48D4F79-1DA2-4C91-A9EE-FDCAEC09E808}.Release|Any CPU.Build.0 = Release|Any CPU {D48D4F79-1DA2-4C91-A9EE-FDCAEC09E808}.Release|Any CPU.Build.0 = Release|Any CPU
{D48D4F79-1DA2-4C91-A9EE-FDCAEC09E808}.Release|x64.ActiveCfg = Release|Any CPU {D48D4F79-1DA2-4C91-A9EE-FDCAEC09E808}.Release|x64.ActiveCfg = Release|Any CPU
{D48D4F79-1DA2-4C91-A9EE-FDCAEC09E808}.Release|x64.Build.0 = Release|Any CPU {D48D4F79-1DA2-4C91-A9EE-FDCAEC09E808}.Release|x64.Build.0 = Release|Any CPU
{7689CBF8-1992-467D-AD45-E1464F705220}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7689CBF8-1992-467D-AD45-E1464F705220}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7689CBF8-1992-467D-AD45-E1464F705220}.Debug|x64.ActiveCfg = Debug|x64
{7689CBF8-1992-467D-AD45-E1464F705220}.Debug|x64.Build.0 = Debug|x64
{7689CBF8-1992-467D-AD45-E1464F705220}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7689CBF8-1992-467D-AD45-E1464F705220}.Release|Any CPU.Build.0 = Release|Any CPU
{7689CBF8-1992-467D-AD45-E1464F705220}.Release|x64.ActiveCfg = Release|x64
{7689CBF8-1992-467D-AD45-E1464F705220}.Release|x64.Build.0 = Release|x64
{116160B2-7D6D-40A2-839C-7997BC0E1A0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{116160B2-7D6D-40A2-839C-7997BC0E1A0C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{116160B2-7D6D-40A2-839C-7997BC0E1A0C}.Debug|x64.ActiveCfg = Debug|Any CPU
{116160B2-7D6D-40A2-839C-7997BC0E1A0C}.Debug|x64.Build.0 = Debug|Any CPU
{116160B2-7D6D-40A2-839C-7997BC0E1A0C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{116160B2-7D6D-40A2-839C-7997BC0E1A0C}.Release|Any CPU.Build.0 = Release|Any CPU
{116160B2-7D6D-40A2-839C-7997BC0E1A0C}.Release|x64.ActiveCfg = Release|Any CPU
{116160B2-7D6D-40A2-839C-7997BC0E1A0C}.Release|x64.Build.0 = Release|Any CPU
{ACAF0A16-FC51-4369-BFA8-484FF20707D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ACAF0A16-FC51-4369-BFA8-484FF20707D7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ACAF0A16-FC51-4369-BFA8-484FF20707D7}.Debug|x64.ActiveCfg = Debug|x64
{ACAF0A16-FC51-4369-BFA8-484FF20707D7}.Debug|x64.Build.0 = Debug|x64
{ACAF0A16-FC51-4369-BFA8-484FF20707D7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ACAF0A16-FC51-4369-BFA8-484FF20707D7}.Release|Any CPU.Build.0 = Release|Any CPU
{ACAF0A16-FC51-4369-BFA8-484FF20707D7}.Release|x64.ActiveCfg = Release|x64
{ACAF0A16-FC51-4369-BFA8-484FF20707D7}.Release|x64.Build.0 = Release|x64
{057644EF-1407-4C2B-808A-AEF0F2979EA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {057644EF-1407-4C2B-808A-AEF0F2979EA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{057644EF-1407-4C2B-808A-AEF0F2979EA8}.Debug|Any CPU.Build.0 = Debug|Any CPU {057644EF-1407-4C2B-808A-AEF0F2979EA8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{057644EF-1407-4C2B-808A-AEF0F2979EA8}.Debug|x64.ActiveCfg = Debug|Any CPU {057644EF-1407-4C2B-808A-AEF0F2979EA8}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -211,6 +165,22 @@ Global
{7337BC9A-ED07-463D-8FCD-A82896CEC6BE}.Release|Any CPU.Build.0 = Release|Any CPU {7337BC9A-ED07-463D-8FCD-A82896CEC6BE}.Release|Any CPU.Build.0 = Release|Any CPU
{7337BC9A-ED07-463D-8FCD-A82896CEC6BE}.Release|x64.ActiveCfg = Release|Any CPU {7337BC9A-ED07-463D-8FCD-A82896CEC6BE}.Release|x64.ActiveCfg = Release|Any CPU
{7337BC9A-ED07-463D-8FCD-A82896CEC6BE}.Release|x64.Build.0 = Release|Any CPU {7337BC9A-ED07-463D-8FCD-A82896CEC6BE}.Release|x64.Build.0 = Release|Any CPU
{9CA7E35C-B5BC-4743-8732-1D8912AA12D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9CA7E35C-B5BC-4743-8732-1D8912AA12D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9CA7E35C-B5BC-4743-8732-1D8912AA12D8}.Debug|x64.ActiveCfg = Debug|Any CPU
{9CA7E35C-B5BC-4743-8732-1D8912AA12D8}.Debug|x64.Build.0 = Debug|Any CPU
{9CA7E35C-B5BC-4743-8732-1D8912AA12D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9CA7E35C-B5BC-4743-8732-1D8912AA12D8}.Release|Any CPU.Build.0 = Release|Any CPU
{9CA7E35C-B5BC-4743-8732-1D8912AA12D8}.Release|x64.ActiveCfg = Release|Any CPU
{9CA7E35C-B5BC-4743-8732-1D8912AA12D8}.Release|x64.Build.0 = Release|Any CPU
{CB85D4B3-EEA2-431F-A47F-1DE7FB0A4802}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CB85D4B3-EEA2-431F-A47F-1DE7FB0A4802}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CB85D4B3-EEA2-431F-A47F-1DE7FB0A4802}.Debug|x64.ActiveCfg = Debug|Any CPU
{CB85D4B3-EEA2-431F-A47F-1DE7FB0A4802}.Debug|x64.Build.0 = Debug|Any CPU
{CB85D4B3-EEA2-431F-A47F-1DE7FB0A4802}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CB85D4B3-EEA2-431F-A47F-1DE7FB0A4802}.Release|Any CPU.Build.0 = Release|Any CPU
{CB85D4B3-EEA2-431F-A47F-1DE7FB0A4802}.Release|x64.ActiveCfg = Release|Any CPU
{CB85D4B3-EEA2-431F-A47F-1DE7FB0A4802}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@@ -222,6 +192,8 @@ Global
{AA3A42D2-0502-41D3-929A-BAB729DF07D6} = {3597B5C5-45B9-4ECB-92A3-D0FFBE47920A} {AA3A42D2-0502-41D3-929A-BAB729DF07D6} = {3597B5C5-45B9-4ECB-92A3-D0FFBE47920A}
{414956B8-DBD4-414C-ABD3-565580739646} = {3597B5C5-45B9-4ECB-92A3-D0FFBE47920A} {414956B8-DBD4-414C-ABD3-565580739646} = {3597B5C5-45B9-4ECB-92A3-D0FFBE47920A}
{7337BC9A-ED07-463D-8FCD-A82896CEC6BE} = {3597B5C5-45B9-4ECB-92A3-D0FFBE47920A} {7337BC9A-ED07-463D-8FCD-A82896CEC6BE} = {3597B5C5-45B9-4ECB-92A3-D0FFBE47920A}
{9CA7E35C-B5BC-4743-8732-1D8912AA12D8} = {3597B5C5-45B9-4ECB-92A3-D0FFBE47920A}
{CB85D4B3-EEA2-431F-A47F-1DE7FB0A4802} = {3597B5C5-45B9-4ECB-92A3-D0FFBE47920A}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {AF00D64E-3C70-474A-8A81-E9E48017C4B5} SolutionGuid = {AF00D64E-3C70-474A-8A81-E9E48017C4B5}

View File

@@ -41,6 +41,20 @@ namespace Modbus.Net
return ans; return ans;
} }
public static ContentType? GetContent<ContentType>(string path, string key) where ContentType : class
{
var root = configuration.GetSection("Modbus.Net");
var firstColon = path.IndexOf(":");
while (firstColon != -1)
{
root = root?.GetSection(path.Substring(0, firstColon));
path = path.Substring(firstColon + 1);
firstColon = path.IndexOf(":");
}
root = root?.GetSection(path);
return root?.GetSection(key).Get<ContentType>();
}
/// <summary> /// <summary>
/// 根据路径,直接查找路径上是否有该元素 /// 根据路径,直接查找路径上是否有该元素
/// </summary> /// </summary>

View File

@@ -22,15 +22,16 @@ namespace Modbus.Net
/// <summary> /// <summary>
/// 读取设备列表 /// 读取设备列表
/// </summary> /// </summary>
/// <param name="machineSection">读取设备的块名</param>
/// <returns>设备的列表</returns> /// <returns>设备的列表</returns>
public static List<IMachine<string>> ReadMachines() public static List<IMachine<string>> ReadMachines(string machineSection = "Machine")
{ {
var ans = new List<IMachine<string>>(); var ans = new List<IMachine<string>>();
var root = configuration.GetSection("Modbus.Net").GetSection("Machine").GetChildren(); var root = configuration.GetSection("Modbus.Net").GetSection(machineSection).GetChildren();
foreach (var machine in root) foreach (var machine in root)
{ {
List<KeyValuePair<string, string>> kv = new List<KeyValuePair<string, string>>(); var kv = new List<KeyValuePair<string, string>>();
Dictionary<string, string> dic = new Dictionary<string, string>(); var dic = new Dictionary<string, string>();
foreach (var paramO in machine.GetChildren()) foreach (var paramO in machine.GetChildren())
{ {
foreach (var param in paramO.GetChildren()) foreach (var param in paramO.GetChildren())
@@ -47,7 +48,10 @@ namespace Modbus.Net
{ {
case "protocol": case "protocol":
{ {
paramsSet.Add(Enum.Parse(Assembly.Load("Modbus.Net." + dic["protocol"]).GetType("Modbus.Net." + dic["protocol"] + "." + dic["protocol"] + "Type"), dic["type"])); if (dic["type"] != null && dic["type"] != "")
{
paramsSet.Add(Enum.Parse(Assembly.Load("Modbus.Net." + dic["protocol"]).GetType("Modbus.Net." + dic["protocol"] + "." + dic["protocol"] + "Type"), dic["type"]));
}
break; break;
} }
case "type": case "type":
@@ -83,23 +87,7 @@ namespace Modbus.Net
default: default:
{ {
string value = param.Value; string value = param.Value;
bool boolValue; paramsSet.Add(value);
byte byteValue;
if (!bool.TryParse(value, out boolValue))
{
if (!byte.TryParse(value, out byteValue))
{
paramsSet.Add(value);
}
else
{
paramsSet.Add(byteValue);
}
}
else
{
paramsSet.Add(boolValue);
}
break; break;
} }
} }
@@ -108,14 +96,30 @@ namespace Modbus.Net
Type machineType = Assembly.Load("Modbus.Net." + dic["protocol"]).GetType("Modbus.Net." + dic["protocol"] + "." + dic["protocol"] + "Machine`2"); Type machineType = Assembly.Load("Modbus.Net." + dic["protocol"]).GetType("Modbus.Net." + dic["protocol"] + "." + dic["protocol"] + "Machine`2");
Type[] typeParams = new Type[] { typeof(string), typeof(string) }; Type[] typeParams = new Type[] { typeof(string), typeof(string) };
Type constructedType = machineType.MakeGenericType(typeParams); Type constructedType = machineType.MakeGenericType(typeParams);
IMachine<string> machineInstance = Activator.CreateInstance(constructedType, paramsSet.ToArray()) as IMachine<string>; var constructorParams = constructedType.GetConstructors().First(p => p.GetParameters().Count() == paramsSet.Count()).GetParameters();
var constructorParamsEnumerator = constructorParams.GetEnumerator();
var paramsSetFinal = new List<object>();
foreach(object paramSet in paramsSet)
{
var moveNext = constructorParamsEnumerator.MoveNext();
var constructor = constructorParamsEnumerator.Current as ParameterInfo;
if (constructor.ParameterType != typeof(string) && paramSet.GetType() == typeof(string))
{
paramsSetFinal.Add(Convert.ChangeType(paramSet, constructor.ParameterType));
}
else
{
paramsSetFinal.Add(paramSet);
}
}
IMachine<string> machineInstance = Activator.CreateInstance(constructedType, paramsSetFinal.ToArray()) as IMachine<string>;
ans.Add(machineInstance); ans.Add(machineInstance);
} }
return ans; return ans;
} }
} }
internal class AddressReader<TUnitKey, TAddressKey, TSubAddressKey> where TUnitKey : IEquatable<TUnitKey> where TAddressKey : IEquatable<TAddressKey> where TSubAddressKey : IEquatable<TSubAddressKey> public class AddressReader<TUnitKey, TAddressKey, TSubAddressKey> where TUnitKey : IEquatable<TUnitKey> where TAddressKey : IEquatable<TAddressKey> where TSubAddressKey : IEquatable<TSubAddressKey>
{ {
private static readonly IConfigurationRoot configuration = new ConfigurationBuilder() private static readonly IConfigurationRoot configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory()) .SetBasePath(Directory.GetCurrentDirectory())

View File

@@ -1,4 +1,5 @@
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Modbus.Net namespace Modbus.Net
@@ -6,7 +7,7 @@ namespace Modbus.Net
/// <inheritdoc /> /// <inheritdoc />
public abstract partial class BaseConnector : BaseConnector<byte[], byte[]> public abstract partial class BaseConnector : BaseConnector<byte[], byte[]>
{ {
private static readonly ILogger<EventHandlerConnector> logger = LogProvider.CreateLogger<EventHandlerConnector>(); private static readonly ILogger<BaseConnector> logger = LogProvider.CreateLogger<BaseConnector>();
} }
/// <summary> /// <summary>
@@ -14,18 +15,10 @@ namespace Modbus.Net
/// </summary> /// </summary>
public abstract class BaseConnector<TParamIn, TParamOut> : IConnectorWithController<TParamIn, TParamOut> where TParamIn : class public abstract class BaseConnector<TParamIn, TParamOut> : IConnectorWithController<TParamIn, TParamOut> where TParamIn : class
{ {
/// <summary>
/// 数据返回代理参数
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
/// <returns></returns>
public delegate MessageReturnCallbackArgs<TParamIn> MessageReturnDelegate(object sender, MessageReturnArgs<TParamOut> args);
/// <summary> /// <summary>
/// 数据返回代理 /// 数据返回代理
/// </summary> /// </summary>
public event MessageReturnDelegate MessageReturn; public Func<MessageReturnArgs<TParamOut>, MessageReturnCallbackArgs<TParamIn>> MessageReturn { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public void AddController(IController controller) public void AddController(IController controller)
@@ -76,7 +69,7 @@ namespace Modbus.Net
/// <returns></returns> /// <returns></returns>
protected TParamIn InvokeReturnMessage(TParamOut receiveMessage) protected TParamIn InvokeReturnMessage(TParamOut receiveMessage)
{ {
return MessageReturn?.Invoke(this, new MessageReturnArgs<TParamOut> { ReturnMessage = receiveMessage })?.SendMessage; return MessageReturn?.Invoke(new MessageReturnArgs<TParamOut> { ReturnMessage = receiveMessage })?.SendMessage;
} }
} }
} }

View File

@@ -212,7 +212,7 @@ namespace Modbus.Net
/// <returns>串口实际读入数据个数 </returns> /// <returns>串口实际读入数据个数 </returns>
public int ReadComm(out byte[] readBuf, int bufRoom, int howTime, int byteTime) public int ReadComm(out byte[] readBuf, int bufRoom, int howTime, int byteTime)
{ {
readBuf = new byte[1023]; readBuf = new byte[10230];
Array.Clear(readBuf, 0, readBuf.Length); Array.Clear(readBuf, 0, readBuf.Length);
if (SerialPort.IsOpen == false) if (SerialPort.IsOpen == false)
@@ -278,7 +278,7 @@ namespace Modbus.Net
} }
// Release unmanaged resources // Release unmanaged resources
Controller?.SendStop(); Controller?.SendStop();
ReceiveMsgThreadStop(); ReceiveMsgThreadStop();
Linkers?.Remove((_slave, _com)); Linkers?.Remove((_slave, _com));
logger.LogInformation("Com connector {ConnectionToken} Removed", _com); logger.LogInformation("Com connector {ConnectionToken} Removed", _com);
if (Linkers?.Count(p => p.Item2 == _com) == 0) if (Linkers?.Count(p => p.Item2 == _com) == 0)
@@ -288,12 +288,12 @@ namespace Modbus.Net
SerialPort?.Close(); SerialPort?.Close();
} }
SerialPort?.Dispose(); SerialPort?.Dispose();
logger.LogInformation("Com interface {Com} Disposed", _com); logger.LogInformation("Com interface {Com} Disposed", _com);
if (Connectors.ContainsKey(_com)) if (Connectors.ContainsKey(_com))
{ {
Connectors[_com] = null; Connectors[_com] = null;
Connectors.Remove(_com); Connectors.Remove(_com);
} }
} }
} }
@@ -500,7 +500,7 @@ namespace Modbus.Net
if (_receiveThread == null) if (_receiveThread == null)
{ {
_receiveThreadCancel = new CancellationTokenSource(); _receiveThreadCancel = new CancellationTokenSource();
_receiveThread = Task.Run(async ()=>await ReceiveMessage(_receiveThreadCancel.Token), _receiveThreadCancel.Token); _receiveThread = Task.Run(async () => await ReceiveMessage(_receiveThreadCancel.Token), _receiveThreadCancel.Token);
try try
{ {
await _receiveThread; await _receiveThread;

View File

@@ -32,18 +32,10 @@ namespace Modbus.Net
/// </summary> /// </summary>
public abstract class EventHandlerConnector<TParamIn, TParamOut> : ChannelHandlerAdapter, IConnectorWithController<TParamIn, TParamOut> where TParamIn : class public abstract class EventHandlerConnector<TParamIn, TParamOut> : ChannelHandlerAdapter, IConnectorWithController<TParamIn, TParamOut> where TParamIn : class
{ {
/// <summary>
/// 数据返回代理参数
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
/// <returns></returns>
public delegate MessageReturnCallbackArgs<TParamIn> MessageReturnDelegate(object sender, MessageReturnArgs<TParamOut> args);
/// <summary> /// <summary>
/// 数据返回代理 /// 数据返回代理
/// </summary> /// </summary>
public event MessageReturnDelegate MessageReturn; public Func<MessageReturnArgs<TParamOut>, MessageReturnCallbackArgs<TParamIn>> MessageReturn { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public void AddController(IController controller) public void AddController(IController controller)
@@ -84,7 +76,7 @@ namespace Modbus.Net
/// <returns></returns> /// <returns></returns>
protected TParamIn InvokeReturnMessage(TParamOut receiveMessage) protected TParamIn InvokeReturnMessage(TParamOut receiveMessage)
{ {
return MessageReturn?.Invoke(this, new MessageReturnArgs<TParamOut> { ReturnMessage = receiveMessage })?.SendMessage; return MessageReturn?.Invoke(new MessageReturnArgs<TParamOut> { ReturnMessage = receiveMessage })?.SendMessage;
} }
} }
} }

View File

@@ -118,7 +118,8 @@ namespace Modbus.Net
pipeline.AddLast("handler", this); pipeline.AddLast("handler", this);
})); }));
Channel = await bootstrap.ConnectAsync(new IPEndPoint(IPAddress.Parse(_host), _port)); var isIp = IPAddress.TryParse(_host, out _);
Channel = await bootstrap.ConnectAsync(isIp ? new IPEndPoint(IPAddress.Parse(_host), _port) : new DnsEndPoint(_host, _port));
if (Channel.Open) if (Channel.Open)
{ {

View File

@@ -180,7 +180,8 @@ namespace Modbus.Net
logger.LogDebug($"Udp client {ConnectionToken} send: {string.Concat(datagram.Select(p => " " + p.ToString("X2")))}"); logger.LogDebug($"Udp client {ConnectionToken} send: {string.Concat(datagram.Select(p => " " + p.ToString("X2")))}");
IByteBuffer buffer = Unpooled.Buffer(); IByteBuffer buffer = Unpooled.Buffer();
buffer.WriteBytes(datagram); buffer.WriteBytes(datagram);
var packet = new DatagramPacket((IByteBuffer)buffer.Retain(), new IPEndPoint(IPAddress.Parse(_host), _port)); var isIp = IPAddress.TryParse(_host, out _);
var packet = new DatagramPacket((IByteBuffer)buffer.Retain(), isIp ? new IPEndPoint(IPAddress.Parse(_host), _port) : new DnsEndPoint(_host, _port));
await Channel.WriteAndFlushAsync(packet); await Channel.WriteAndFlushAsync(packet);
} }
catch (Exception err) catch (Exception err)

View File

@@ -1,4 +1,3 @@
using Quartz.Logging;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -106,8 +105,8 @@ namespace Modbus.Net
finally finally
{ {
_sendingThreadCancel.Dispose(); _sendingThreadCancel.Dispose();
_sendingThreadCancel = null; _sendingThreadCancel = null;
} }
} }
} }

View File

@@ -0,0 +1,242 @@
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Modbus.Net
{
/// <summary>
/// 控制器基类
/// </summary>
public class NoResponseController : IController
{
private static readonly ILogger<NoResponseController> logger = LogProvider.CreateLogger<NoResponseController>();
/// <summary>
/// 等待的消息队列
/// </summary>
protected List<MessageWaitingDef> WaitingMessages { get; set; }
/// <summary>
/// 消息维护线程
/// </summary>
protected Task SendingThread { get; set; }
/// <summary>
/// 间隔时间
/// </summary>
public int AcquireTime { get; }
/// <summary>
/// 消息维护线程是否在运行
/// </summary>
public virtual bool IsSending => SendingThread != null;
private MessageWaitingDef _currentSendingPos;
private CancellationTokenSource _sendingThreadCancel;
/// <summary>
/// 构造器
/// </summary>
public NoResponseController(int acquireTime)
{
WaitingMessages = new List<MessageWaitingDef>();
AcquireTime = acquireTime;
}
/// <inheritdoc />
public MessageWaitingDef AddMessage(byte[] sendMessage)
{
var def = new MessageWaitingDef
{
Key = GetKeyFromMessage(sendMessage)?.Item1,
SendMessage = sendMessage,
SendMutex = new AutoResetEvent(false),
ReceiveMutex = new AutoResetEvent(false)
};
if (AddMessageToList(def))
{
return def;
}
return null;
}
/// <summary>
/// 发送消息的实际内部方法
/// </summary>
protected void SendingMessageControlInner(CancellationToken token)
{
while (true)
{
if (AcquireTime > 0)
{
Thread.Sleep(AcquireTime);
}
lock (WaitingMessages)
{
try
{
if (_currentSendingPos == null)
{
if (WaitingMessages.Count > 0)
{
_currentSendingPos = WaitingMessages.First();
_currentSendingPos.SendMutex.Set();
_currentSendingPos.ReceiveMessage = new byte[0];
_currentSendingPos.ReceiveMutex.Set();
ForceRemoveWaitingMessage(_currentSendingPos);
}
}
else
{
if (WaitingMessages.Count <= 0)
{
_currentSendingPos = null;
}
else if (WaitingMessages.IndexOf(_currentSendingPos) == -1)
{
_currentSendingPos = WaitingMessages.First();
_currentSendingPos.SendMutex.Set();
_currentSendingPos.ReceiveMessage = new byte[0];
_currentSendingPos.ReceiveMutex.Set();
ForceRemoveWaitingMessage(_currentSendingPos);
}
}
}
catch (ObjectDisposedException e)
{
logger.LogError(e, "Controller _currentSendingPos disposed");
_currentSendingPos = null;
}
catch (Exception e)
{
logger.LogError(e, "Controller throws exception");
SendStop();
}
}
if (token.IsCancellationRequested)
{
token.ThrowIfCancellationRequested();
}
}
}
/// <inheritdoc />
public virtual void SendStop()
{
Clear();
_sendingThreadCancel?.Cancel();
if (SendingThread != null)
{
while (!SendingThread.IsCanceled)
{
Thread.Sleep(10);
}
SendingThread.Dispose();
SendingThread = null;
}
Clear();
}
/// <inheritdoc />
public virtual async void SendStart()
{
if (!IsSending)
{
_sendingThreadCancel = new CancellationTokenSource();
SendingThread = Task.Run(() => SendingMessageControlInner(_sendingThreadCancel.Token), _sendingThreadCancel.Token);
try
{
await SendingThread;
}
catch (OperationCanceledException)
{ }
finally
{
_sendingThreadCancel.Dispose();
_sendingThreadCancel = null;
}
}
}
/// <inheritdoc />
public void Clear()
{
if (WaitingMessages != null)
{
lock (WaitingMessages)
{
WaitingMessages.Clear();
}
}
}
/// <summary>
/// 将信息添加到队列
/// </summary>
/// <param name="def">需要添加的信息信息</param>
protected virtual bool AddMessageToList(MessageWaitingDef def)
{
var ans = false;
lock (WaitingMessages)
{
if (WaitingMessages.FirstOrDefault(p => p.Key == def.Key) == null || def.Key == null)
{
WaitingMessages.Add(def);
ans = true;
}
}
return ans;
}
/// <summary>
/// 获取信息的检索关键字
/// </summary>
/// <param name="message">待确认的信息</param>
/// <returns>信息的检索关键字</returns>
protected (string, string)? GetKeyFromMessage(byte[] message)
{
return null;
}
/// <inheritdoc />
public ICollection<(byte[], bool)> ConfirmMessage(byte[] receiveMessage)
{
var ans = new List<(byte[], bool)>
{
(receiveMessage, true)
};
return ans;
}
/// <summary>
/// 从等待队列中匹配信息
/// </summary>
/// <param name="receiveMessage">返回的信息</param>
/// <returns>从等待队列中匹配的信息</returns>
protected MessageWaitingDef GetMessageFromWaitingList(byte[] receiveMessage)
{
MessageWaitingDef ans;
lock (WaitingMessages)
{
ans = WaitingMessages.FirstOrDefault();
}
return ans;
}
/// <inheritdoc />
public void ForceRemoveWaitingMessage(MessageWaitingDef def)
{
lock (WaitingMessages)
{
if (WaitingMessages.IndexOf(def) >= 0)
{
WaitingMessages.Remove(def);
}
}
}
}
}

View File

@@ -36,5 +36,34 @@ namespace Modbus.Net
} }
throw new NotImplementedException(controllerName + " not found exception"); throw new NotImplementedException(controllerName + " not found exception");
} }
/// <summary>
/// 添加一个Controller
/// </summary>
/// <param name="protocolLinker">ProtocolLinker实例</param>
/// <param name="constructorParams">参数</param>
/// <param name="connector">Connector实例</param>
/// <exception cref="NotImplementedException">如果没有发现控制器,报错</exception>
public static void AddController(this IProtocolReceiver<byte[], byte[]> protocolReceiver, object[] constructorParams, IConnector<byte[], byte[]> connector)
{
IController controller = null;
var assemblies = AssemblyHelper.GetAllLibraryAssemblies();
string controllerName = protocolReceiver.GetType().Name.Substring(0, protocolReceiver.GetType().Name.Length - 16) + "ResponseController";
foreach (var assembly in assemblies)
{
var controllerType = assembly.GetType(assembly.GetName().Name + "." + controllerName);
if (controllerType != null)
{
controller = assembly.CreateInstance(controllerType.FullName, true, BindingFlags.Default, null, constructorParams, null, null) as IController;
break;
}
}
if (controller != null)
{
((IConnectorWithController<byte[], byte[]>)connector).AddController(controller);
return;
}
throw new NotImplementedException(controllerName + " not found exception");
}
} }
} }

View File

@@ -12,6 +12,16 @@ namespace Modbus.Net
/// </summary> /// </summary>
public abstract class ValueHelper public abstract class ValueHelper
{ {
/// <summary>
/// 是否为小端格式
/// </summary>
public abstract bool LittleEndian { get; }
/// <summary>
/// 位是否为小端格式
/// </summary>
public abstract bool LittleEndianBit { get; }
/// <summary> /// <summary>
/// 兼容数据类型对应的字节长度 /// 兼容数据类型对应的字节长度
/// </summary> /// </summary>
@@ -347,12 +357,12 @@ namespace Modbus.Net
/// <summary> /// <summary>
/// 协议中的内容构造是否小端的,默认是小端构造协议。 /// 协议中的内容构造是否小端的,默认是小端构造协议。
/// </summary> /// </summary>
public static bool LittleEndian => true; public override bool LittleEndian => true;
/// <summary> /// <summary>
/// 协议中的比特位内容构造是否小端的,默认是小端构造协议。 /// 协议中的比特位内容构造是否小端的,默认是小端构造协议。
/// </summary> /// </summary>
public static bool LittleEndianBit => true; public override bool LittleEndianBit => true;
/// <summary> /// <summary>
/// 将一个byte数字转换为一个byte元素的数组。 /// 将一个byte数字转换为一个byte元素的数组。
@@ -1228,7 +1238,7 @@ namespace Modbus.Net
/// <summary> /// <summary>
/// 是否为大端 /// 是否为大端
/// </summary> /// </summary>
protected new bool LittleEndian => false; public override bool LittleEndian => false;
/// <summary> /// <summary>
/// 覆盖的获取实例的方法 /// 覆盖的获取实例的方法
@@ -1471,12 +1481,12 @@ namespace Modbus.Net
/// <summary> /// <summary>
/// 是否为小端 /// 是否为小端
/// </summary> /// </summary>
protected new bool LittleEndian => false; public override bool LittleEndian => false;
/// <summary> /// <summary>
/// 是否为小端位 /// 是否为小端位
/// </summary> /// </summary>
protected new bool LittleEndianBit => false; public override bool LittleEndianBit => false;
/// <summary> /// <summary>
/// 覆盖的实例获取方法 /// 覆盖的实例获取方法

View File

@@ -1,3 +1,4 @@
using System;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Modbus.Net namespace Modbus.Net
@@ -5,8 +6,13 @@ namespace Modbus.Net
/// <summary> /// <summary>
/// 基础的协议连接接口 /// 基础的协议连接接口
/// </summary> /// </summary>
public interface IConnector<in TParamIn, TParamOut> public interface IConnector<TParamIn, TParamOut>
{ {
/// <summary>
/// 数据返回代理
/// </summary>
Func<MessageReturnArgs<TParamOut>, MessageReturnCallbackArgs<TParamIn>> MessageReturn { get; set; }
/// <summary> /// <summary>
/// 标识Connector的连接关键字 /// 标识Connector的连接关键字
/// </summary> /// </summary>

View File

@@ -3,7 +3,7 @@
/// <summary> /// <summary>
/// 基础的协议连接接口 /// 基础的协议连接接口
/// </summary> /// </summary>
public interface IConnectorWithController<in TParamIn, TParamOut> : IConnector<TParamIn, TParamOut> public interface IConnectorWithController<TParamIn, TParamOut> : IConnector<TParamIn, TParamOut>
{ {
/// <summary> /// <summary>
/// 增加传输控制器 /// 增加传输控制器

View File

@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Modbus.Net.Interface
{
/// <summary>
/// Machine的数据读写接口
/// </summary>
public interface IMachineServerMethodDatas : IMachineMethod
{
/// <summary>
/// 从站发送事件
/// </summary>
event EventHandler ServerMessageEvent;
/// <summary>
/// 读取数据
/// </summary>
/// <returns>从设备读取的数据</returns>
Task<ReturnStruct<Dictionary<string, ReturnUnit<double>>>> ServerReadDatasAsync(MachineDataType getDataType);
/// <summary>
/// 写入数据
/// </summary>
/// <param name="setDataType">写入类型</param>
/// <param name="values">需要写入的数据字典当写入类型为Address时键为需要写入的地址当写入类型为CommunicationTag时键为需要写入的单元的描述</param>
/// <returns>是否写入成功</returns>
Task<ReturnStruct<bool>> ServerUploadDatasAsync(MachineDataType setDataType, Dictionary<string, double> values);
}
}

View File

@@ -0,0 +1,61 @@
using System;
using System.Threading.Tasks;
namespace Modbus.Net
{
/// <summary>
/// 协议接收器接口
/// </summary>
/// <typeparam name="TParamIn">从Receiver传出的数据类型</typeparam>
/// <typeparam name="TParamOut">向Receiver传入的数据类型</typeparam>
public interface IProtocolReceiver<TParamIn, TParamOut>
{
/// <summary>
/// 转发事件
/// </summary>
Func<TParamOut, TParamIn> DispatchEvent { get; }
/// <summary>
/// 通讯字符串
/// </summary>
string ConnectionToken { get; }
/// <summary>
/// 设备是否连接
/// </summary>
bool IsConnected { get; }
/// <summary>
/// 连接设备
/// </summary>
/// <returns>设备是否连接成功</returns>
Task<bool> ConnectAsync();
/// <summary>
/// 断开设备
/// </summary>
/// <returns>设备是否断开成功</returns>
bool Disconnect();
/// <summary>
/// 接收并发送数据
/// </summary>
/// <param name="content">接收协议的内容</param>
/// <returns>发送协议的内容</returns>
TParamIn ReceiveSend(TParamOut content);
/// <summary>
/// 接收并发送数据,不进行协议扩展和收缩,用于特殊协议
/// </summary>
/// <param name="content">发送协议的内容</param>
/// <returns>接收协议的内容</returns>
TParamIn ReceiveSendWithoutExtAndDec(TParamOut content);
/// <summary>
/// 检查接收的数据是否正确
/// </summary>
/// <param name="content">接收协议的内容</param>
/// <returns>协议是否是正确的</returns>
bool? CheckRight(TParamOut content);
}
}

View File

@@ -21,8 +21,9 @@ namespace Modbus.Net
/// </summary> /// </summary>
/// <param name="startAddress">开始地址</param> /// <param name="startAddress">开始地址</param>
/// <param name="getByteCount">获取字节数个数</param> /// <param name="getByteCount">获取字节数个数</param>
/// <param name="getOriginalCount">获取原始个数</param>
/// <returns>接收到的byte数据</returns> /// <returns>接收到的byte数据</returns>
Task<ReturnStruct<byte[]>> GetDatasAsync(string startAddress, int getByteCount); Task<ReturnStruct<byte[]>> GetDatasAsync(string startAddress, int getByteCount, int getOriginalCount);
/// <summary> /// <summary>
/// 获取数据 /// 获取数据
@@ -53,7 +54,8 @@ namespace Modbus.Net
/// </summary> /// </summary>
/// <param name="startAddress">开始地址</param> /// <param name="startAddress">开始地址</param>
/// <param name="setContents">设置数据</param> /// <param name="setContents">设置数据</param>
/// <param name="setOriginalCount">设置原始长度</param>
/// <returns>是否设置成功</returns> /// <returns>是否设置成功</returns>
Task<ReturnStruct<bool>> SetDatasAsync(string startAddress, object[] setContents); Task<ReturnStruct<bool>> SetDatasAsync(string startAddress, object[] setContents, int setOriginalCount);
} }
} }

View File

@@ -0,0 +1,9 @@
namespace Modbus.Net
{
/// <summary>
/// Api入口的抽象
/// </summary>
public interface IUtilityServer : IUtilityProperty, IUtilityServerMethodDatas
{
}
}

View File

@@ -0,0 +1,33 @@
using System.Threading.Tasks;
namespace Modbus.Net
{
/// <summary>
/// Utility方法读写接口
/// </summary>
public interface IUtilityServerMethod
{
}
/// <summary>
/// Utility的数据读写接口
/// </summary>
public interface IUtilityServerMethodDatas : IUtilityServerMethod
{
/// <summary>
/// 获取数据
/// </summary>
/// <param name="startAddress">开始地址</param>
/// <param name="getByteCount">获取字节数个数</param>
/// <returns>接收到的byte数据</returns>
Task<ReturnStruct<byte[]>> GetServerDatasAsync(string startAddress, int getByteCount);
/// <summary>
/// 设置数据
/// </summary>
/// <param name="startAddress">开始地址</param>
/// <param name="setContents">设置数据</param>
/// <returns>是否设置成功</returns>
Task<ReturnStruct<bool>> SetServerDatasAsync(string startAddress, object[] setContents);
}
}

View File

@@ -31,7 +31,15 @@ namespace Modbus.Net
connectionTimeout = int.Parse(connectionTimeout != null ? connectionTimeout.ToString() : null ?? ConfigurationReader.GetValue("COM:" + com, "ConnectionTimeout")); connectionTimeout = int.Parse(connectionTimeout != null ? connectionTimeout.ToString() : null ?? ConfigurationReader.GetValue("COM:" + com, "ConnectionTimeout"));
isFullDuplex = bool.Parse(isFullDuplex != null ? isFullDuplex.ToString() : null ?? ConfigurationReader.GetValue("COM:" + com, "FullDuplex")); isFullDuplex = bool.Parse(isFullDuplex != null ? isFullDuplex.ToString() : null ?? ConfigurationReader.GetValue("COM:" + com, "FullDuplex"));
BaseConnector = new ComConnector(com + ":" + slaveAddress, baudRate.Value, parity.Value, stopBits.Value, dataBits.Value, handshake.Value, connectionTimeout.Value, isFullDuplex.Value); BaseConnector = new ComConnector(com + ":" + slaveAddress, baudRate.Value, parity.Value, stopBits.Value, dataBits.Value, handshake.Value, connectionTimeout.Value, isFullDuplex.Value);
this.AddController(new object[2] { com, slaveAddress }, BaseConnector); var noResponse = bool.Parse(ConfigurationReader.GetValue("COM:" + com, "NoResponse") ?? ConfigurationReader.GetValue("Controller", "NoResponse"));
if (noResponse)
{
((IConnectorWithController<byte[], byte[]>)BaseConnector).AddController(new NoResponseController(int.Parse(ConfigurationReader.GetValue("COM:" + com + ":" + slaveAddress, "FetchSleepTime"))));
}
else
{
this.AddController(new object[2] { com, slaveAddress }, BaseConnector);
}
} }
} }
} }

View File

@@ -0,0 +1,220 @@
using System;
using System.IO.Ports;
using System.Reflection;
using System.Threading.Tasks;
namespace Modbus.Net
{
/// <summary>
/// 基本的协议连接器
/// </summary>
public abstract class ProtocolReceiver : ProtocolReceiver<byte[], byte[]>
{
protected ProtocolReceiver(string com, int slaveAddress, BaudRate? baudRate = null, Parity? parity = null, StopBits? stopBits = null, DataBits? dataBits = null, Handshake? handshake = null,
int? connectionTimeout = null, bool? isFullDuplex = null)
{
baudRate = Enum.Parse<BaudRate>(baudRate != null ? baudRate.ToString() : null ?? ConfigurationReader.GetValue("COM:" + com, "BaudRate"));
parity = Enum.Parse<Parity>(parity != null ? parity.ToString() : null ?? ConfigurationReader.GetValue("COM:" + com, "Parity"));
stopBits = Enum.Parse<StopBits>(stopBits != null ? stopBits.ToString() : null ?? ConfigurationReader.GetValue("COM:" + com, "StopBits"));
dataBits = Enum.Parse<DataBits>(dataBits != null ? dataBits.ToString() : null ?? ConfigurationReader.GetValue("COM:" + com, "DataBits"));
handshake = Enum.Parse<Handshake>(handshake != null ? handshake.ToString() : null ?? ConfigurationReader.GetValue("COM:" + com, "Handshake"));
connectionTimeout = int.Parse(connectionTimeout != null ? connectionTimeout.ToString() : null ?? ConfigurationReader.GetValue("COM:" + com, "ConnectionTimeout"));
isFullDuplex = bool.Parse(isFullDuplex != null ? isFullDuplex.ToString() : null ?? ConfigurationReader.GetValue("COM:" + com, "FullDuplex"));
BaseConnector = new ComConnector(com + ":" + slaveAddress, baudRate.Value, parity.Value, stopBits.Value, dataBits.Value, handshake.Value, connectionTimeout.Value, isFullDuplex.Value);
var noResponse = bool.Parse(ConfigurationReader.GetValue("COM:" + com, "NoResponse") ?? ConfigurationReader.GetValue("Controller", "NoResponse"));
if (noResponse)
{
((IConnectorWithController<byte[], byte[]>)BaseConnector).AddController(new NoResponseController(int.Parse(ConfigurationReader.GetValue("COM:" + com + ":" + slaveAddress, "FetchSleepTime"))));
}
else
{
this.AddController(new object[2] { com, slaveAddress }, BaseConnector);
}
BaseConnector.MessageReturn = receiveMessage => new MessageReturnCallbackArgs<byte[]>() { SendMessage = ReceiveSend(receiveMessage.ReturnMessage) };
}
/// <summary>
/// 发送并接收数据
/// </summary>
/// <param name="content">发送协议的内容</param>
/// <returns>接收协议的内容</returns>
public override byte[] ReceiveSend(byte[] content)
{
var checkRight = CheckRight(content);
if (checkRight == true)
{
var decBytes = BytesDecact(content);
var explainContent = DataExplain(decBytes);
var returnBytes = DataProcess(explainContent);
if (returnBytes != null)
{
var extBytes = BytesExtend(returnBytes);
return extBytes;
}
return null;
}
else
{
return null;
}
}
/// <summary>
/// 协议内容扩展,发送时根据需要扩展
/// </summary>
/// <param name="content">扩展前的基本协议内容</param>
/// <returns>扩展后的协议内容</returns>
public virtual byte[] BytesExtend(byte[] content)
{
//自动查找相应的协议放缩类,命令规则为——当前的实际类名(注意是继承后的)+"BytesExtend"。
var bytesExtend =
Activator.CreateInstance(GetType().GetTypeInfo().Assembly.GetType(GetType().FullName + "BytesExtend"))
as
IProtocolLinkerBytesExtend<byte[], byte[]>;
return bytesExtend?.BytesExtend(content);
}
/// <summary>
/// 协议内容缩减,接收时根据需要缩减
/// </summary>
/// <param name="content">缩减前的完整协议内容</param>
/// <returns>缩减后的协议内容</returns>
public virtual byte[] BytesDecact(byte[] content)
{
//自动查找相应的协议放缩类,命令规则为——当前的实际类名(注意是继承后的)+"BytesExtend"。
var bytesExtend =
Activator.CreateInstance(GetType().GetTypeInfo().Assembly.GetType(GetType().FullName + "BytesExtend"))
as
IProtocolLinkerBytesExtend<byte[], byte[]>;
return bytesExtend?.BytesDecact(content);
}
/// <summary>
/// 检查接收的数据是否正确
/// </summary>
/// <param name="content">接收协议的内容</param>
/// <returns>协议是否是正确的</returns>
public override bool? CheckRight(byte[] content)
{
if (content == null)
{
Disconnect();
return false;
}
if (content.Length == 0) return null;
return true;
}
protected abstract Func<byte[], ReceiveDataDef> DataExplain { get; }
public Func<ReceiveDataDef, byte[]> DataProcess { get; set; } = null;
}
public abstract class ProtocolReceiver<TParamIn, TParamOut> : IProtocolReceiver<TParamIn, TParamOut>
where TParamIn : class
{
/// <summary>
/// 传输连接器
/// </summary>
protected IConnector<TParamIn, TParamOut> BaseConnector;
/// <summary>
/// 连接设备
/// </summary>
/// <returns>设备是否连接成功</returns>
public async Task<bool> ConnectAsync()
{
return await BaseConnector.ConnectAsync();
}
/// <summary>
/// 断开设备
/// </summary>
/// <returns>设备是否断开成功</returns>
public bool Disconnect()
{
return BaseConnector.Disconnect();
}
/// <summary>
/// 通讯字符串
/// </summary>
public string ConnectionToken => BaseConnector.ConnectionToken;
/// <summary>
/// 设备是否连接
/// </summary>
public bool IsConnected => BaseConnector != null && BaseConnector.IsConnected;
public virtual Func<TParamOut, TParamIn> DispatchEvent
{
get
{
return receiveContent => ReceiveSend(receiveContent);
}
}
/// <summary>
/// 发送并接收数据
/// </summary>
/// <param name="content">发送协议的内容</param>
/// <returns>接收协议的内容</returns>
public virtual TParamIn ReceiveSend(TParamOut content)
{
return ReceiveSendWithoutExtAndDec(content);
}
/// <summary>
/// 发送并接收数据,不进行协议扩展和收缩,用于特殊协议
/// </summary>
/// <param name="content">发送协议的内容</param>
/// <returns>接收协议的内容</returns>
public virtual TParamIn ReceiveSendWithoutExtAndDec(TParamOut content)
{
var checkRight = CheckRight(content);
if (checkRight == true)
{
if (DispatchEvent != null)
{
var returnContent = DispatchEvent(content);
return returnContent;
}
else
{
return null;
}
}
else
{
return null;
}
}
/// <summary>
/// 检查接收的数据是否正确
/// </summary>
/// <param name="content">接收协议的内容</param>
/// <returns>协议是否是正确的</returns>
public virtual bool? CheckRight(TParamOut content)
{
if (content != null) return true;
Disconnect();
return false;
}
}
public class ReceiveDataDef
{
public byte SlaveAddress { get; set; }
public byte FunctionCode { get; set; }
public ushort StartAddress { get; set; }
public ushort Count { get; set; }
public byte WriteByteCount { get; set; }
public byte[] WriteContent { get; set; }
}
}

View File

@@ -18,7 +18,15 @@
isFullDuplex = bool.Parse(isFullDuplex != null ? isFullDuplex.ToString() : null ?? ConfigurationReader.GetValue("TCP:" + ip + ":" + port, "FullDuplex")); isFullDuplex = bool.Parse(isFullDuplex != null ? isFullDuplex.ToString() : null ?? ConfigurationReader.GetValue("TCP:" + ip + ":" + port, "FullDuplex"));
//初始化连接对象 //初始化连接对象
BaseConnector = new TcpConnector(ip, port, connectionTimeout.Value, isFullDuplex.Value); BaseConnector = new TcpConnector(ip, port, connectionTimeout.Value, isFullDuplex.Value);
this.AddController(new object[2] { ip, port }, BaseConnector); var noResponse = bool.Parse(ConfigurationReader.GetValue("TCP:" + ip + ":" + port, "NoResponse") ?? ConfigurationReader.GetValue("Controller", "NoResponse"));
if (noResponse)
{
((IConnectorWithController<byte[], byte[]>)BaseConnector).AddController(new NoResponseController(int.Parse(ConfigurationReader.GetValue("TCP:" + ip + ":" + port, "FetchSleepTime"))));
}
else
{
this.AddController(new object[2] { ip, port }, BaseConnector);
}
} }
} }
} }

View File

@@ -18,7 +18,15 @@
isFullDuplex = bool.Parse(isFullDuplex != null ? isFullDuplex.ToString() : null ?? ConfigurationReader.GetValue("UDP:" + ip + ":" + port, "FullDuplex")); isFullDuplex = bool.Parse(isFullDuplex != null ? isFullDuplex.ToString() : null ?? ConfigurationReader.GetValue("UDP:" + ip + ":" + port, "FullDuplex"));
//初始化连接对象 //初始化连接对象
BaseConnector = new UdpConnector(ip, port, connectionTimeout.Value, isFullDuplex.Value); BaseConnector = new UdpConnector(ip, port, connectionTimeout.Value, isFullDuplex.Value);
this.AddController(new object[2] { ip, port }, BaseConnector); var noResponse = bool.Parse(ConfigurationReader.GetValue("UDP:" + ip + ":" + port, "NoResponse") ?? ConfigurationReader.GetValue("Controller", "NoResponse"));
if (noResponse)
{
((IConnectorWithController<byte[], byte[]>)BaseConnector).AddController(new NoResponseController(int.Parse(ConfigurationReader.GetValue("UDP:" + ip + ":" + port, "FetchSleepTime"))));
}
else
{
this.AddController(new object[2] { ip, port }, BaseConnector);
}
} }
} }
} }

View File

@@ -125,6 +125,7 @@ namespace Modbus.Net
preNum - (int)Math.Floor(initNum), preNum - (int)Math.Floor(initNum),
AddressTranslator.GetAreaByteLength(address.Area), AddressTranslator.GetAreaByteLength(address.Area),
ValueHelper.ByteLength[preType.FullName])), ValueHelper.ByteLength[preType.FullName])),
GetOriginalCount = groupedAddress.Count(),
DataType = typeof(byte), DataType = typeof(byte),
OriginalAddresses = originalAddresses.ToList() OriginalAddresses = originalAddresses.ToList()
}); });
@@ -154,6 +155,7 @@ namespace Modbus.Net
AddressHelper.MapProtocolGetCountToAbstractByteCount( AddressHelper.MapProtocolGetCountToAbstractByteCount(
preNum - (int)Math.Floor(initNum), AddressTranslator.GetAreaByteLength(area), preNum - (int)Math.Floor(initNum), AddressTranslator.GetAreaByteLength(area),
ValueHelper.ByteLength[preType.FullName])), ValueHelper.ByteLength[preType.FullName])),
GetOriginalCount = groupedAddress.Count(),
DataType = typeof(byte), DataType = typeof(byte),
OriginalAddresses = originalAddresses.ToList() OriginalAddresses = originalAddresses.ToList()
}); });
@@ -200,6 +202,7 @@ namespace Modbus.Net
(int) (int)
Math.Ceiling(newByteCount / Math.Ceiling(newByteCount /
ValueHelper.ByteLength[communicationUnit.DataType.FullName]), ValueHelper.ByteLength[communicationUnit.DataType.FullName]),
GetOriginalCount = newOriginalAddresses.Count,
OriginalAddresses = newOriginalAddresses OriginalAddresses = newOriginalAddresses
}; };
@@ -215,6 +218,7 @@ namespace Modbus.Net
(int) (int)
Math.Ceiling(oldByteCount / Math.Ceiling(oldByteCount /
ValueHelper.ByteLength[communicationUnit.DataType.FullName]); ValueHelper.ByteLength[communicationUnit.DataType.FullName]);
communicationUnit.GetOriginalCount = oldOriginalAddresses.Count;
communicationUnit.OriginalAddresses = oldOriginalAddresses; communicationUnit.OriginalAddresses = oldOriginalAddresses;
newAns.Add(communicationUnit); newAns.Add(communicationUnit);
} }
@@ -244,6 +248,7 @@ namespace Modbus.Net
SubAddress = address.SubAddress, SubAddress = address.SubAddress,
DataType = address.DataType, DataType = address.DataType,
GetCount = 1, GetCount = 1,
GetOriginalCount = 1,
OriginalAddresses = new List<AddressUnit<TKey, TAddressKey, TSubAddressKey>> { address } OriginalAddresses = new List<AddressUnit<TKey, TAddressKey, TSubAddressKey>> { address }
}).ToList(); }).ToList();
} }
@@ -345,6 +350,7 @@ namespace Modbus.Net
orderedGap.GapNumber + orderedGap.GapNumber +
(int) (int)
(nowAddress.GetCount * ValueHelper.ByteLength[nowAddress.DataType.FullName]), (nowAddress.GetCount * ValueHelper.ByteLength[nowAddress.DataType.FullName]),
GetOriginalCount = preAddress.GetOriginalCount + nowAddress.GetOriginalCount + orderedGap.GapNumber,
DataType = typeof(byte), DataType = typeof(byte),
OriginalAddresses = preAddress.OriginalAddresses.ToList().Union(nowAddress.OriginalAddresses) OriginalAddresses = preAddress.OriginalAddresses.ToList().Union(nowAddress.OriginalAddresses)
}; };
@@ -392,4 +398,59 @@ namespace Modbus.Net
addressUnits); addressUnits);
} }
} }
public class ReadAddressesDesc
{
public string Area { get; set; }
public int Address { get; set; }
public int GetCount { get; set; }
}
/// <summary>
/// 固定读取地址的范围,无视所有其它规则
/// </summary>
public class AddressCombinerStatic<TKey> : AddressCombiner<TKey, int, int> where TKey : IEquatable<TKey>
{
protected IEnumerable<ReadAddressesDesc> ReadAddresses { get; set; }
protected AddressTranslator AddressTranslator { get; set; }
public AddressCombinerStatic(IEnumerable<ReadAddressesDesc> readAddresses, AddressTranslator addressTranslator)
{
AddressTranslator = addressTranslator;
ReadAddresses = readAddresses;
}
/// <summary>
/// 组合地址
/// </summary>
/// <param name="addresses">需要组合的地址</param>
/// <returns>组合后的地址</returns>
public override IEnumerable<CommunicationUnit<TKey, int, int>> Combine(IEnumerable<AddressUnit<TKey, int, int>> addresses)
{
List<CommunicationUnit<TKey, int, int>> communicationUnits = new List<CommunicationUnit<TKey, int, int>>();
foreach (var readAddress in ReadAddresses)
{
var originalAddresses = new List<AddressUnit<TKey, int, int>>();
foreach (var address in addresses)
{
if (address.Area == readAddress.Area && address.Address >= readAddress.Address && address.Address < readAddress.Address + readAddress.GetCount)
originalAddresses.Add(address);
}
communicationUnits.Add(new CommunicationUnit<TKey, int, int>
{
Area = readAddress.Area,
Address = readAddress.Address,
GetCount = readAddress.GetCount * (int)
Math.Ceiling(AddressTranslator.GetAreaByteLength(readAddress.Area)),
DataType = typeof(byte),
GetOriginalCount = originalAddresses.Count,
OriginalAddresses = originalAddresses.ToList()
});
}
return communicationUnits;
}
}
} }

View File

@@ -25,8 +25,8 @@ namespace Modbus.Net
/// <param name="keepConnect">是否保持连接</param> /// <param name="keepConnect">是否保持连接</param>
/// <param name="slaveAddress">从站地址</param> /// <param name="slaveAddress">从站地址</param>
/// <param name="masterAddress">主站地址</param> /// <param name="masterAddress">主站地址</param>
protected BaseMachine(TKey id, IEnumerable<AddressUnit<TUnitKey, int, int>> getAddresses, bool keepConnect, byte slaveAddress, protected BaseMachine(TKey id, string alias, IEnumerable<AddressUnit<TUnitKey, int, int>> getAddresses, bool keepConnect, byte slaveAddress,
byte masterAddress) : base(id, getAddresses, keepConnect) byte masterAddress) : base(id, alias, getAddresses, keepConnect)
{ {
SlaveAddress = slaveAddress; SlaveAddress = slaveAddress;
MasterAddress = masterAddress; MasterAddress = masterAddress;
@@ -96,11 +96,22 @@ namespace Modbus.Net
(int) (int)
Math.Ceiling(communicateAddress.GetCount * Math.Ceiling(communicateAddress.GetCount *
ValueHelper.ByteLength[ ValueHelper.ByteLength[
communicateAddress.DataType.FullName])); communicateAddress.DataType.FullName]),
communicateAddress.GetOriginalCount);
//如果已知没有返回,终止
if (datas.IsSuccess == null)
{
return new ReturnStruct<Dictionary<string, ReturnUnit<double>>>()
{
Datas = null,
IsSuccess = null,
ErrorCode = datas.ErrorCode,
ErrorMsg = datas.ErrorMsg
};
}
//如果没有数据,终止 //如果没有数据,终止
if (datas.IsSuccess == false || datas.Datas == null) else if (datas.IsSuccess == false || datas.Datas == null)
{ {
return new ReturnStruct<Dictionary<string, ReturnUnit<double>>>() return new ReturnStruct<Dictionary<string, ReturnUnit<double>>>()
{ {
@@ -138,6 +149,10 @@ namespace Modbus.Net
var localMainPos = (int)localPos; var localMainPos = (int)localPos;
//字节坐标的子地址位置 //字节坐标的子地址位置
var localSubPos = (int)((localPos - localMainPos) * 8); var localSubPos = (int)((localPos - localMainPos) * 8);
if (ValueHelper.GetInstance(BaseUtility.Endian).LittleEndianBit == true && AddressTranslator.GetAreaByteLength(communicateAddress.Area) > 1 && ValueHelper.ByteLength[address.DataType.FullName] < AddressTranslator.GetAreaByteLength(communicateAddress.Area))
{
localMainPos =(int)(2 * ((int)(localMainPos / AddressTranslator.GetAreaByteLength(communicateAddress.Area)) * AddressTranslator.GetAreaByteLength(communicateAddress.Area)) + (AddressTranslator.GetAreaByteLength(communicateAddress.Area) - 1) - localMainPos);
}
//根据类型选择返回结果的键是通讯标识还是地址 //根据类型选择返回结果的键是通讯标识还是地址
string key; string key;
@@ -330,12 +345,21 @@ namespace Modbus.Net
(int) (int)
Math.Ceiling(communicateAddress.GetCount * Math.Ceiling(communicateAddress.GetCount *
ValueHelper.ByteLength[ ValueHelper.ByteLength[
communicateAddress.DataType.FullName])); communicateAddress.DataType.FullName]),
communicateAddress.GetOriginalCount);
var valueHelper = ValueHelper.GetInstance(BaseUtility.Endian); var valueHelper = ValueHelper.GetInstance(BaseUtility.Endian);
//如果设备本身能获取到数据但是没有数据 //如果设备本身能获取到数据但是没有数据
var datas = datasReturn; var datas = datasReturn;
//没有返回直接设0
if (datas.IsSuccess == null)
{
datas.Datas = new byte[(int)
Math.Ceiling(communicateAddress.GetCount *
ValueHelper.ByteLength[
communicateAddress.DataType.FullName])];
}
//如果没有数据,终止 //如果没有数据,终止
if (datas.IsSuccess == false || datas.Datas == null) if (datas.IsSuccess == false || datas.Datas == null)
{ {
@@ -439,7 +463,7 @@ namespace Modbus.Net
await await
BaseUtility.GetUtilityMethods<IUtilityMethodDatas>().SetDatasAsync(addressStart, BaseUtility.GetUtilityMethods<IUtilityMethodDatas>().SetDatasAsync(addressStart,
valueHelper.ByteArrayToObjectArray(datas.Datas, valueHelper.ByteArrayToObjectArray(datas.Datas,
new KeyValuePair<Type, int>(communicateAddress.DataType, communicateAddress.GetCount))); new KeyValuePair<Type, int>(communicateAddress.DataType, communicateAddress.GetCount)), communicateAddress.GetOriginalCount);
} }
//如果不保持连接,断开连接 //如果不保持连接,断开连接
if (!KeepConnect) if (!KeepConnect)
@@ -490,8 +514,8 @@ namespace Modbus.Net
/// </summary> /// </summary>
/// <param name="id">设备的ID号</param> /// <param name="id">设备的ID号</param>
/// <param name="getAddresses">需要与设备通讯的地址</param> /// <param name="getAddresses">需要与设备通讯的地址</param>
protected BaseMachine(TKey id, IEnumerable<AddressUnit<TUnitKey, TAddressKey, TSubAddressKey>> getAddresses) protected BaseMachine(TKey id, string alias, IEnumerable<AddressUnit<TUnitKey, TAddressKey, TSubAddressKey>> getAddresses)
: this(id, getAddresses, false) : this(id, alias, getAddresses, false)
{ {
} }
@@ -501,11 +525,22 @@ namespace Modbus.Net
/// <param name="id">设备的ID号</param> /// <param name="id">设备的ID号</param>
/// <param name="getAddresses">需要与设备通讯的地址</param> /// <param name="getAddresses">需要与设备通讯的地址</param>
/// <param name="keepConnect">是否保持连接</param> /// <param name="keepConnect">是否保持连接</param>
protected BaseMachine(TKey id, IEnumerable<AddressUnit<TUnitKey, TAddressKey, TSubAddressKey>> getAddresses, bool keepConnect) protected BaseMachine(TKey id, string alias, IEnumerable<AddressUnit<TUnitKey, TAddressKey, TSubAddressKey>> getAddresses, bool keepConnect)
{ {
Id = id; Id = id;
GetAddresses = getAddresses; GetAddresses = getAddresses;
KeepConnect = keepConnect; KeepConnect = keepConnect;
if (alias.Contains(':'))
{
var aliasArray = alias.Split(':');
ProjectName = aliasArray[0];
MachineName = aliasArray[1];
}
else
{
ProjectName = "";
MachineName = alias;
}
} }
private readonly int _maxErrorCount = 3; private readonly int _maxErrorCount = 3;
@@ -617,6 +652,9 @@ namespace Modbus.Net
AddressFormater.FormatAddress(address.Area, address.Address, AddressFormater.FormatAddress(address.Area, address.Address,
address.SubAddress), address.SubAddress),
(int) (int)
Math.Ceiling(ValueHelper.ByteLength[
address.DataType.FullName]),
(int)
Math.Ceiling(ValueHelper.ByteLength[ Math.Ceiling(ValueHelper.ByteLength[
address.DataType.FullName])); address.DataType.FullName]));
@@ -876,7 +914,7 @@ namespace Modbus.Net
//写入数据 //写入数据
await await
BaseUtility.GetUtilityMethods<IUtilityMethodDatas>().SetDatasAsync(address, BaseUtility.GetUtilityMethods<IUtilityMethodDatas>().SetDatasAsync(address,
new object[] { data }); new object[] { data } , 1);
} }
//如果不保持连接,断开连接 //如果不保持连接,断开连接
if (!KeepConnect) if (!KeepConnect)
@@ -1006,6 +1044,11 @@ namespace Modbus.Net
/// </summary> /// </summary>
public int GetCount { get; set; } public int GetCount { get; set; }
/// <summary>
/// 获取原始个数
/// </summary>
public int GetOriginalCount { get; set; }
/// <summary> /// <summary>
/// 数据类型 /// 数据类型
/// </summary> /// </summary>

View File

@@ -39,6 +39,7 @@
<PackageReference Include="DotNetty.Handlers" Version="0.7.5" /> <PackageReference Include="DotNetty.Handlers" Version="0.7.5" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.4" /> <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.4" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Nito.AsyncEx" Version="5.1.2" /> <PackageReference Include="Nito.AsyncEx" Version="5.1.2" />
<PackageReference Include="Quartz" Version="3.6.3" /> <PackageReference Include="Quartz" Version="3.6.3" />
<PackageReference Include="System.IO.Ports" Version="7.0.0" /> <PackageReference Include="System.IO.Ports" Version="7.0.0" />

View File

@@ -185,7 +185,12 @@ namespace Modbus.Net
public virtual async Task<T> SendReceiveAsync<T>(TProtocolUnit unit, IInputStruct content) public virtual async Task<T> SendReceiveAsync<T>(TProtocolUnit unit, IInputStruct content)
where T : class, IOutputStruct where T : class, IOutputStruct
{ {
return (await SendReceiveAsync(unit, content)).Unwrap<T>(); var ans = await SendReceiveAsync(unit, content);
if (ans.Success == null)
{
return null;
}
return ans.Unwrap<T>();
} }
/// <summary> /// <summary>

View File

@@ -27,7 +27,7 @@ namespace Modbus.Net
/// <param name="parameters">传递给输入结构的参数</param> /// <param name="parameters">传递给输入结构的参数</param>
/// <param name="success">上次的管道是否成功执行</param> /// <param name="success">上次的管道是否成功执行</param>
protected PipeUnit(IProtocolLinker<byte[], byte[]> protocolLinker, ProtocolUnit<byte[], byte[]> protocolUnit, byte[] parameters, protected PipeUnit(IProtocolLinker<byte[], byte[]> protocolLinker, ProtocolUnit<byte[], byte[]> protocolUnit, byte[] parameters,
bool success) : base(protocolLinker, protocolUnit, parameters, success) bool? success) : base(protocolLinker, protocolUnit, parameters, success)
{ {
} }
@@ -39,7 +39,7 @@ namespace Modbus.Net
/// <returns>发送完成之后新的管道实例</returns> /// <returns>发送完成之后新的管道实例</returns>
public async Task<PipeUnit> SendReceiveAsync(Endian endian, Func<byte[], object[]> inputStructCreator) public async Task<PipeUnit> SendReceiveAsync(Endian endian, Func<byte[], object[]> inputStructCreator)
{ {
if (Success) if (Success == true)
{ {
var content = inputStructCreator.Invoke(ReturnParams); var content = inputStructCreator.Invoke(ReturnParams);
if (ProtocolLinker != null) if (ProtocolLinker != null)
@@ -47,7 +47,7 @@ namespace Modbus.Net
await ProtocolLinker.SendReceiveAsync(ProtocolUnit<byte[], byte[]>.TranslateContent(endian, content)), await ProtocolLinker.SendReceiveAsync(ProtocolUnit<byte[], byte[]>.TranslateContent(endian, content)),
true); true);
} }
return new PipeUnit(ProtocolLinker, null, ReturnParams, false); return new PipeUnit(ProtocolLinker, null, ReturnParams, Success);
} }
/// <summary> /// <summary>
@@ -62,8 +62,18 @@ namespace Modbus.Net
{ {
var receiveContent = await SendReceiveAsyncParamOut(unit, inputStructCreator); var receiveContent = await SendReceiveAsyncParamOut(unit, inputStructCreator);
if (receiveContent != null) if (receiveContent != null)
return new PipeUnit(ProtocolLinker, unit, {
receiveContent, true); if (receiveContent.Length > 0)
{
return new PipeUnit(ProtocolLinker, unit,
receiveContent, true);
}
else
{
return new PipeUnit(ProtocolLinker, unit,
receiveContent, null);
}
}
return new PipeUnit(ProtocolLinker, unit, ReturnParams, return new PipeUnit(ProtocolLinker, unit, ReturnParams,
false); false);
} }
@@ -106,7 +116,7 @@ namespace Modbus.Net
/// <param name="protocolUnit">协议单元</param> /// <param name="protocolUnit">协议单元</param>
/// <param name="parameters">输入参数</param> /// <param name="parameters">输入参数</param>
/// <param name="success">上一次管道结果是否成功</param> /// <param name="success">上一次管道结果是否成功</param>
protected PipeUnit(TProtocolLinker protocolLinker, TProtocolUnit protocolUnit, TParamOut parameters, bool success) protected PipeUnit(TProtocolLinker protocolLinker, TProtocolUnit protocolUnit, TParamOut parameters, bool? success)
{ {
ProtocolLinker = protocolLinker; ProtocolLinker = protocolLinker;
ProtocolUnit = protocolUnit; ProtocolUnit = protocolUnit;
@@ -132,7 +142,7 @@ namespace Modbus.Net
/// <summary> /// <summary>
/// 本次管道是否成功 /// 本次管道是否成功
/// </summary> /// </summary>
public bool Success { get; } public bool? Success { get; }
/// <summary> /// <summary>
/// 向设备发送数据,返回输出参数 /// 向设备发送数据,返回输出参数
@@ -143,7 +153,7 @@ namespace Modbus.Net
protected async Task<TParamOut> SendReceiveAsyncParamOut(TProtocolUnit unit, protected async Task<TParamOut> SendReceiveAsyncParamOut(TProtocolUnit unit,
Func<TParamOut, IInputStruct> inputStructCreator) Func<TParamOut, IInputStruct> inputStructCreator)
{ {
if (Success) if (Success == true)
{ {
var content = inputStructCreator.Invoke(ReturnParams); var content = inputStructCreator.Invoke(ReturnParams);
var formatContent = unit.Format(content); var formatContent = unit.Format(content);

View File

@@ -61,7 +61,7 @@ Manage several types of Protocol to a same calling interface.
### Machine ### Machine
Shows the Hardware PLC or other types of machine and implement a high level send and receive api. Shows the Hardware DCS or other types of machine and implement a high level send and receive api.
### Job ### Job

View File

@@ -13,7 +13,7 @@
/// <summary> /// <summary>
/// 操作是否成功 /// 操作是否成功
/// </summary> /// </summary>
public bool IsSuccess { get; set; } public bool? IsSuccess { get; set; }
/// <summary> /// <summary>
/// 错误代码 /// 错误代码
/// </summary> /// </summary>

View File

@@ -50,8 +50,9 @@ namespace Modbus.Net
/// </summary> /// </summary>
/// <param name="startAddress">开始地址</param> /// <param name="startAddress">开始地址</param>
/// <param name="getByteCount">获取字节数个数</param> /// <param name="getByteCount">获取字节数个数</param>
/// <param name="getOriginalCount">获取原始个数</param>
/// <returns>接收到的byte数据</returns> /// <returns>接收到的byte数据</returns>
public abstract Task<ReturnStruct<byte[]>> GetDatasAsync(string startAddress, int getByteCount); public abstract Task<ReturnStruct<byte[]>> GetDatasAsync(string startAddress, int getByteCount, int getOriginalCount);
/// <summary> /// <summary>
/// 获取数据 /// 获取数据
@@ -67,7 +68,7 @@ namespace Modbus.Net
var typeName = getTypeAndCount.Key.FullName; var typeName = getTypeAndCount.Key.FullName;
var bCount = ValueHelper.ByteLength[typeName]; var bCount = ValueHelper.ByteLength[typeName];
var getReturnValue = await GetDatasAsync(startAddress, var getReturnValue = await GetDatasAsync(startAddress,
(int)Math.Ceiling(bCount * getTypeAndCount.Value)); (int)Math.Ceiling(bCount * getTypeAndCount.Value), getTypeAndCount.Value);
var getBytes = getReturnValue; var getBytes = getReturnValue;
if (getBytes.IsSuccess == false || getBytes.Datas == null) if (getBytes.IsSuccess == false || getBytes.Datas == null)
{ {
@@ -105,15 +106,15 @@ namespace Modbus.Net
/// </summary> /// </summary>
/// <typeparam name="T">需要接收的类型</typeparam> /// <typeparam name="T">需要接收的类型</typeparam>
/// <param name="startAddress">开始地址</param> /// <param name="startAddress">开始地址</param>
/// <param name="getByteCount">获取字节数个数</param> /// <param name="getCount">获取个数</param>
/// <returns>接收到的对应的类型和数据</returns> /// <returns>接收到的对应的类型和数据</returns>
public virtual async Task<ReturnStruct<T[]>> GetDatasAsync<T>(string startAddress, public virtual async Task<ReturnStruct<T[]>> GetDatasAsync<T>(string startAddress,
int getByteCount) int getCount)
{ {
try try
{ {
var getBytes = await GetDatasAsync(startAddress, var getBytes = await GetDatasAsync(startAddress,
new KeyValuePair<Type, int>(typeof(T), getByteCount)); new KeyValuePair<Type, int>(typeof(T), getCount));
if (getBytes.IsSuccess == false || getBytes.Datas == null) if (getBytes.IsSuccess == false || getBytes.Datas == null)
{ {
return new ReturnStruct<T[]> return new ReturnStruct<T[]>
@@ -162,7 +163,7 @@ namespace Modbus.Net
let typeName = getTypeAndCount.Key.FullName let typeName = getTypeAndCount.Key.FullName
let bCount = ValueHelper.ByteLength[typeName] let bCount = ValueHelper.ByteLength[typeName]
select (int)Math.Ceiling(bCount * getTypeAndCount.Value)).Sum(); select (int)Math.Ceiling(bCount * getTypeAndCount.Value)).Sum();
var getReturnValue = await GetDatasAsync(startAddress, bAllCount); var getReturnValue = await GetDatasAsync(startAddress, bAllCount, bAllCount);
var getBytes = getReturnValue; var getBytes = getReturnValue;
if (getBytes.IsSuccess == false || getBytes.Datas == null) if (getBytes.IsSuccess == false || getBytes.Datas == null)
{ {
@@ -200,8 +201,9 @@ namespace Modbus.Net
/// </summary> /// </summary>
/// <param name="startAddress">开始地址</param> /// <param name="startAddress">开始地址</param>
/// <param name="setContents">设置数据</param> /// <param name="setContents">设置数据</param>
/// <param name="setOriginalCount">设置原始长度</param>
/// <returns>是否设置成功</returns> /// <returns>是否设置成功</returns>
public abstract Task<ReturnStruct<bool>> SetDatasAsync(string startAddress, object[] setContents); public abstract Task<ReturnStruct<bool>> SetDatasAsync(string startAddress, object[] setContents, int setOriginalCount);
/// <summary> /// <summary>
/// 协议是否遵循小端格式 /// 协议是否遵循小端格式

View File

@@ -0,0 +1,120 @@
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;
namespace Modbus.Net
{
/// <summary>
/// 基础Api入口
/// </summary>
public abstract class BaseUtilityServer<TParamIn, TParamOut, TProtocolUnit, TPipeUnit> : IUtilityServer
where TProtocolUnit : class, IProtocolFormatting<TParamIn, TParamOut> where TParamOut : class
where TPipeUnit : PipeUnit<TParamIn, TParamOut, IProtocolLinker<TParamIn, TParamOut>, TProtocolUnit>
{
private static readonly ILogger<BaseUtilityServer<TParamIn, TParamOut, TProtocolUnit, TPipeUnit>> logger = LogProvider.CreateLogger<BaseUtilityServer<TParamIn, TParamOut, TProtocolUnit, TPipeUnit>>();
/// <summary>
/// 协议收发主体
/// </summary>
protected IProtocol<TParamIn, TParamOut, TProtocolUnit, TPipeUnit> Wrapper;
/// <summary>
/// 构造器
/// </summary>
protected BaseUtilityServer(byte slaveAddress, byte masterAddress)
{
SlaveAddress = slaveAddress;
MasterAddress = masterAddress;
AddressTranslator = new AddressTranslatorBase();
}
/// <summary>
/// 连接字符串
/// </summary>
protected string ConnectionString { get; set; }
/// <summary>
/// 从站号
/// </summary>
public byte SlaveAddress { get; set; }
/// <summary>
/// 主站号
/// </summary>
public byte MasterAddress { get; set; }
/// <summary>
/// 获取数据
/// </summary>
/// <param name="startAddress">开始地址</param>
/// <param name="getByteCount">获取字节数个数</param>
/// <returns>接收到的byte数据</returns>
public abstract Task<ReturnStruct<byte[]>> GetServerDatasAsync(string startAddress, int getByteCount);
/// <summary>
/// 设置数据
/// </summary>
/// <param name="startAddress">开始地址</param>
/// <param name="setContents">设置数据</param>
/// <returns>是否设置成功</returns>
public abstract Task<ReturnStruct<bool>> SetServerDatasAsync(string startAddress, object[] setContents);
/// <summary>
/// 协议是否遵循小端格式
/// </summary>
public abstract Endian Endian { get; }
/// <summary>
/// 设备是否已经连接
/// </summary>
public bool IsConnected => Wrapper?.ProtocolLinker != null && Wrapper.ProtocolLinker.IsConnected;
/// <summary>
/// 标识设备的连接关键字
/// </summary>
public string ConnectionToken
=> Wrapper?.ProtocolLinker == null ? ConnectionString : Wrapper.ProtocolLinker.ConnectionToken;
/// <summary>
/// 地址翻译器
/// </summary>
public AddressTranslator AddressTranslator { get; set; }
/// <summary>
/// 连接设备
/// </summary>
/// <returns>设备是否连接成功</returns>
public async Task<bool> ConnectAsync()
{
return await Wrapper.ConnectAsync();
}
/// <summary>
/// 断开设备
/// </summary>
/// <returns>设备是否断开成功</returns>
public bool Disconnect()
{
return Wrapper.Disconnect();
}
/// <summary>
/// 返回Utility的方法集合
/// </summary>
/// <typeparam name="TUtilityMethod">Utility方法集合类型</typeparam>
/// <returns>Utility方法集合</returns>
public TUtilityMethod GetUtilityMethods<TUtilityMethod>() where TUtilityMethod : class, IUtilityMethod
{
if (this is TUtilityMethod)
{
return this as TUtilityMethod;
}
return null;
}
/// <summary>
/// 设置连接类型
/// </summary>
/// <param name="connectionType">连接类型</param>
public abstract void SetConnectionType(int connectionType);
}
}

View File

@@ -46,7 +46,8 @@
"Host": "opc.tcp://localhost/test" "Host": "opc.tcp://localhost/test"
}, },
"Controller": { "Controller": {
"WaitingListCount": "100" "WaitingListCount": "100",
"NoResponse": false
} }
} }
} }

View File

@@ -19,8 +19,6 @@ The real Modbus Implementation has been moved to [Modbus.Net.Modbus]( https://ww
There is also [Modbus.Net.Siemens]( https://www.nuget.org/packages/Modbus.Net.Siemens) that can communicate with Siemens S7-200, S7-200 Smart, S7-300, S7-400, S7-1200 and S7-1500 using PPI or TCP/IP. There is also [Modbus.Net.Siemens]( https://www.nuget.org/packages/Modbus.Net.Siemens) that can communicate with Siemens S7-200, S7-200 Smart, S7-300, S7-400, S7-1200 and S7-1500 using PPI or TCP/IP.
[Modbus.Net.Opc]( https://www.nuget.org/packages/Modbus.Net.Opc) Implements OPC DA and OPC UA protocol.
Supported Platforms Supported Platforms
------------------- -------------------
* Visual Studio 2022 * Visual Studio 2022
@@ -31,4 +29,3 @@ Thanks
* Quartz - Job Scheduler * Quartz - Job Scheduler
* Serilog - Logging * Serilog - Logging
* DotNetty - Network Transporting * DotNetty - Network Transporting
* h-opc & Technosoftware.DaAeHdaSolution & OPCFoundation.NetStandard - OPC Trasporting

View File

@@ -56,7 +56,7 @@ namespace AnyType.Controllers
Value = 0, Value = 0,
Type = unitValue.DataType.Name Type = unitValue.DataType.Name
}; };
var machine = new ModbusMachine("1", ModbusType.Tcp, "10.10.18.251:502", addressUnits, true, 2, 0, Endian.BigEndianLsb); var machine = new ModbusMachine("1", "", ModbusType.Tcp, "10.10.18.251:502", addressUnits, true, 2, 0, Endian.BigEndianLsb);
//启动任务 //启动任务
await MachineJobSchedulerCreator.CreateScheduler("Trigger1", -1, 1).Result.From(machine.Id, machine, MachineDataType.CommunicationTag).Result.Query("Query1", await MachineJobSchedulerCreator.CreateScheduler("Trigger1", -1, 1).Result.From(machine.Id, machine, MachineDataType.CommunicationTag).Result.Query("Query1",
returnValues => returnValues =>

View File

@@ -46,7 +46,8 @@
"Host": "opc.tcp://localhost/test" "Host": "opc.tcp://localhost/test"
}, },
"Controller": { "Controller": {
"WaitingListCount": "100" "WaitingListCount": "100",
"NoResponse": false
} }
} }
} }

View File

@@ -46,7 +46,8 @@
"Host": "opc.tcp://localhost/test" "Host": "opc.tcp://localhost/test"
}, },
"Controller": { "Controller": {
"WaitingListCount": "100" "WaitingListCount": "100",
"NoResponse": false
} }
} }
} }

View File

@@ -8,8 +8,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.6.0" /> <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.1.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -24,7 +24,6 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\Modbus.Net\Modbus.Net.Modbus\Modbus.Net.Modbus.csproj" /> <ProjectReference Include="..\..\Modbus.Net\Modbus.Net.Modbus\Modbus.Net.Modbus.csproj" />
<ProjectReference Include="..\..\Modbus.Net\Modbus.Net.OPC\Modbus.Net.Opc.csproj" />
<ProjectReference Include="..\..\Modbus.Net\Modbus.Net.Siemens\Modbus.Net.Siemens.csproj" /> <ProjectReference Include="..\..\Modbus.Net\Modbus.Net.Siemens\Modbus.Net.Siemens.csproj" />
<ProjectReference Include="..\..\Modbus.Net\Modbus.Net\Modbus.Net.csproj" /> <ProjectReference Include="..\..\Modbus.Net\Modbus.Net\Modbus.Net.csproj" />
<ProjectReference Include="..\MachineJob.CodeGenerator\MachineJob.CodeGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" /> <ProjectReference Include="..\MachineJob.CodeGenerator\MachineJob.CodeGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />

View File

@@ -93,11 +93,11 @@ namespace MachineJob.Service
private Dictionary<string, double>? QueryConsole(DataReturnDef dataReturnDef) private Dictionary<string, double>? QueryConsole(DataReturnDef dataReturnDef)
{ {
var values = dataReturnDef.ReturnValues.Datas; var values = dataReturnDef.ReturnValues.Datas;
if (dataReturnDef.ReturnValues.IsSuccess) if (dataReturnDef.ReturnValues.IsSuccess == true)
{ {
foreach (var value in values) foreach (var value in values)
{ {
_logger.LogInformation(dataReturnDef.MachineId + " " + value.Key + " " + value.Value.DeviceValue); _logger.LogDebug(dataReturnDef.MachineId + " " + value.Key + " " + value.Value.DeviceValue);
} }
/* /*
@@ -135,7 +135,25 @@ namespace MachineJob.Service
return values.MapGetValuesToSetValues(); return values.MapGetValuesToSetValues();
} }
return null; else if (dataReturnDef.ReturnValues.IsSuccess == null)
{
Random r = new Random();
Dictionary<string, double> ans = new Dictionary<string, double>();
for (int i = 0; i < 10; i++)
{
ans["Test" + (i + 1)] = r.Next(65536) - 32768;
}
return ans;
}
else
{
_logger.LogError(dataReturnDef.MachineId + " Return Error.");
return null;
}
} }
} }
} }

View File

@@ -46,7 +46,8 @@
"Host": "opc.tcp://localhost/test" "Host": "opc.tcp://localhost/test"
}, },
"Controller": { "Controller": {
"WaitingListCount": "100" "WaitingListCount": "100",
"NoResponse": false
} }
} }
} }

View File

@@ -0,0 +1,36 @@
using Quartz.Logging;
namespace ModbusTcpToRtu
{
// simple log provider to get something to the console
public class ConsoleLogProvider : ILogProvider
{
private readonly IConfigurationRoot configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ?? "Production"}.json", true)
.Build();
public Logger GetLogger(string name)
{
return (level, func, exception, parameters) =>
{
if (level >= configuration.GetSection("Quartz").GetValue<Quartz.Logging.LogLevel>("LogLevel") && func != null)
{
Console.WriteLine("[" + DateTime.Now.ToLongTimeString() + "] [" + level + "] " + func(), parameters);
}
return true;
};
}
public IDisposable OpenNestedContext(string message)
{
throw new NotImplementedException();
}
public IDisposable OpenMappedContext(string key, object value, bool destructure = false)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk.Worker">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>dotnet-ModbusTcpToRtu-b7b7d9ed-80ce-4790-86de-5c3cf21e0a2e</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<None Remove="service.bat" />
</ItemGroup>
<ItemGroup>
<Content Include="service.bat">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="7.0.1" />
<PackageReference Include="Serilog.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="7.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Modbus.Net\Modbus.Net.Modbus\Modbus.Net.Modbus.csproj" />
<ProjectReference Include="..\..\Modbus.Net\Modbus.Net\Modbus.Net.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,38 @@
using ModbusTcpToRtu;
using Serilog;
IHost host = Host.CreateDefaultBuilder(args).UseWindowsService()
.ConfigureAppConfiguration((hostingContext, config) =>
{
var configuration = config
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ?? "Production"}.json", true)
.AddEnvironmentVariables()
.Build();
Directory.SetCurrentDirectory(hostingContext.HostingEnvironment.ContentRootPath);
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.File("Log\\log..txt", Serilog.Events.LogEventLevel.Error, shared: true, rollingInterval: RollingInterval.Day)
.CreateLogger();
var loggerFactory = new LoggerFactory().AddSerilog(Log.Logger);
Quartz.Logging.LogProvider.SetCurrentLogProvider(new ConsoleLogProvider());
Modbus.Net.LogProvider.SetLogProvider(loggerFactory);
}
)
.ConfigureServices(services =>
{
services.AddHostedService<Worker>();
services.AddLogging(loggingBuilder => loggingBuilder.AddSerilog(Log.Logger, true));
})
.Build();
await host.RunAsync();

View File

@@ -0,0 +1,163 @@
using Modbus.Net;
using Modbus.Net.Modbus;
using Quartz;
using Quartz.Impl;
using Quartz.Impl.Matchers;
using BaseUtility = Modbus.Net.BaseUtility<byte[], byte[], Modbus.Net.ProtocolUnit<byte[], byte[]>, Modbus.Net.PipeUnit>;
using MultipleMachinesJobScheduler = Modbus.Net.MultipleMachinesJobScheduler<Modbus.Net.IMachineMethodDatas, string, double>;
namespace ModbusTcpToRtu
{
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
private BaseUtility readUtility;
private BaseUtility writeUtility;
public Worker(ILogger<Worker> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var triggerKey = "Modbus.Net.Job.Utility.SchedulerTrigger";
var jobKey = "Modbus.Net.Job.Utility.JobKey";
var intervalMilliSecond = int.Parse(ConfigurationReader.GetValue("Utility", "interval")) * 1000;
var count = int.Parse(ConfigurationReader.GetValue("Utility", "count"));
var readWriteGroup = ConfigurationReader.GetContent<List<ReadWriteGroup>>("Utility", "readwrite");
var readType = Enum.Parse<ModbusType>(ConfigurationReader.GetValue("Utility:read", "type"));
var readAddress = ConfigurationReader.GetValue("Utility:read", "address");
var readSlaveAddress = byte.Parse(ConfigurationReader.GetValue("Utility:read", "slaveAddress"));
var readMasterAddress = byte.Parse(ConfigurationReader.GetValue("Utility:read", "masterAddress"));
var writeType = Enum.Parse<ModbusType>(ConfigurationReader.GetValue("Utility:write", "type"));
var writeAddress = ConfigurationReader.GetValue("Utility:write", "address");
var writeSlaveAddress = byte.Parse(ConfigurationReader.GetValue("Utility:write", "slaveAddress"));
var writeMasterAddress = byte.Parse(ConfigurationReader.GetValue("Utility:write", "masterAddress"));
readUtility = new ModbusUtility(readType, readAddress, readSlaveAddress, readMasterAddress, Endian.BigEndianLsb);
writeUtility = new ModbusUtility(writeType, writeAddress, writeSlaveAddress, writeMasterAddress, Endian.BigEndianLsb);
IScheduler scheduler = await StdSchedulerFactory.GetDefaultScheduler();
await scheduler.Start();
ITrigger trigger;
if (intervalMilliSecond <= 0)
{
trigger = TriggerBuilder.Create()
.WithIdentity(triggerKey, "Modbus.Net.DataQuery.Group." + triggerKey)
.StartNow()
.Build();
}
else if (count >= 0)
trigger = TriggerBuilder.Create()
.WithIdentity(triggerKey, "Modbus.Net.DataQuery.Group." + triggerKey)
.StartNow()
.WithSimpleSchedule(b => b.WithInterval(TimeSpan.FromMilliseconds(intervalMilliSecond)).WithRepeatCount(count))
.Build();
else
trigger = TriggerBuilder.Create()
.WithIdentity(triggerKey, "Modbus.Net.DataQuery.Group." + triggerKey)
.StartNow()
.WithSimpleSchedule(b => b.WithInterval(TimeSpan.FromMilliseconds(intervalMilliSecond)).RepeatForever())
.Build();
IJobListener listener;
if (intervalMilliSecond <= 0)
{
listener = new JobChainingJobLIstenerWithDataMapRepeated("Modbus.Net.DataQuery.Chain." + triggerKey, new string[2] { "Value", "SetValue" }, count);
}
else
{
listener = new JobChainingJobListenerWithDataMap("Modbus.Net.DataQuery.Chain." + triggerKey, new string[2] { "Value", "SetValue" });
}
scheduler.ListenerManager.AddJobListener(listener, GroupMatcher<JobKey>.GroupEquals("Modbus.Net.DataQuery.Group." + triggerKey));
if (await scheduler.GetTrigger(new TriggerKey(triggerKey)) != null)
{
await scheduler.UnscheduleJob(new TriggerKey(triggerKey, "Modbus.Net.DataQuery.Group." + triggerKey));
}
var jobKeys = await scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals("Modbus.Net.DataQuery.Group." + triggerKey));
await scheduler.DeleteJobs(jobKeys);
var job = JobBuilder.Create<UtilityPassDataJob>()
.WithIdentity(jobKey)
.StoreDurably(true)
.Build();
job.JobDataMap.Put("UtilityRead", readUtility);
job.JobDataMap.Put("UtilityReadWriteGroup", readWriteGroup);
job.JobDataMap.Put("UtilityWrite", writeUtility);
await scheduler.ScheduleJob(job, trigger);
}
public override Task StopAsync(CancellationToken cancellationToken)
{
return Task.Run(() => MultipleMachinesJobScheduler.CancelJob());
}
}
public class UtilityPassDataJob : IJob
{
public async Task Execute(IJobExecutionContext context)
{
object utilityReadObject;
object utilityWriteObject;
object utilityReadWriteGroupObject;
context.JobDetail.JobDataMap.TryGetValue("UtilityRead", out utilityReadObject);
context.JobDetail.JobDataMap.TryGetValue("UtilityWrite", out utilityWriteObject);
context.JobDetail.JobDataMap.TryGetValue("UtilityReadWriteGroup", out utilityReadWriteGroupObject);
var readUtility = (BaseUtility)utilityReadObject;
var writeUtility = (BaseUtility)utilityWriteObject;
var utilityReadWriteGroup = (List<ReadWriteGroup>)utilityReadWriteGroupObject;
if (readUtility.IsConnected != true)
await readUtility.ConnectAsync();
if (writeUtility.IsConnected != true)
await writeUtility.ConnectAsync();
foreach (var rwGroup in utilityReadWriteGroup)
{
var datas = await readUtility.GetDatasAsync(rwGroup.ReadStart / 10000 + "X " + rwGroup.ReadStart % 10000, rwGroup.ReadCount * 2, rwGroup.ReadCount);
if (datas.IsSuccess == true)
{
var ans = await writeUtility.SetDatasAsync(rwGroup.WriteStart / 10000 + "X " + rwGroup.WriteStart % 10000, ByteArrayToObjectArray(datas.Datas), rwGroup.ReadCount);
if (ans.Datas)
{
Console.WriteLine("success");
}
}
else
{
Console.WriteLine("failed");
}
}
}
public static object[] ByteArrayToObjectArray(byte[] arrBytes)
{
List<object> objArray = new List<object>();
foreach (byte b in arrBytes)
{
objArray.Add(b);
}
return objArray.ToArray();
}
}
public class ReadWriteGroup
{
public int ReadStart { get; set; }
public int ReadCount { get; set; }
public int WriteStart { get; set; }
}
}

View File

@@ -0,0 +1,21 @@
{
"Serilog": {
"MinimumLevel": {
"Default": "Debug",
"Override": {
"Microsoft": "Debug",
"Microsoft.Hosting.Lifetime": "Debug"
}
}
},
"Logging": {
"LogLevel": {
"Default": "Debug",
"Microsoft": "Debug",
"Microsoft.Hosting.Lifetime": "Debug"
}
},
"Quartz": {
"LogLevel": "Debug"
}
}

View File

@@ -0,0 +1,21 @@
{
"Serilog": {
"MinimumLevel": {
"Default": "Error",
"Override": {
"Microsoft": "Error",
"Microsoft.Hosting.Lifetime": "Error"
}
}
},
"Logging": {
"LogLevel": {
"Default": "Error",
"Microsoft": "Error",
"Microsoft.Hosting.Lifetime": "Error"
}
},
"Quartz": {
"LogLevel": "Error"
}
}

View File

@@ -0,0 +1,53 @@
{
"Modbus.Net": {
"TCP": {
"ConnectionTimeout": "5000",
"FetchSleepTime": "100",
"FullDuplex": "True",
"Modbus": {
"ModbusPort": "502",
"IP": "192.168.1.1"
},
"Siemens": {
"SiemensPort": "102",
"IP": "192.168.1.1"
}
},
"UDP": {
"ConnectionTimeout": "5000",
"FetchSleepTime": "100",
"FullDuplex": "True",
"Modbus": {
"ModbusPort": "502",
"IP": "192.168.1.1"
}
},
"COM": {
"FetchSleepTime": "100",
"ConnectionTimeout": "5000",
"BaudRate": "BaudRate9600",
"Parity": "None",
"StopBits": "One",
"DataBits": "Eight",
"Handshake": "None",
"FullDuplex": "False",
"Modbus": {
"COM": "COM1"
},
"Siemens": {
"COM": "COM2",
"Parity": "Even"
}
},
"OpcDa": {
"Host": "opcda://localhost/test"
},
"OpcUa": {
"Host": "opc.tcp://localhost/test"
},
"Controller": {
"WaitingListCount": "100",
"NoResponse": false
}
}
}

View File

@@ -0,0 +1,55 @@
{
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
}
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"Quartz": {
"LogLevel": "Info"
},
"ConnectionStrings": {
"DatabaseWriteConnectionString": "Server=127.0.0.1; User ID=root; Password=123456; Database=modbusnettest;"
},
"Modbus.Net": {
"Utility": {
"interval": 10, //间隔时常(秒)
"count": -1, //不要动
"readwrite": [
{
"readStart": 40001, //读取开始地址
"readCount": 16, //读取字的个数
"writeStart": 40001 //写入开始地址
}, //可以写多个
{
"readStart": 40016, //读取开始地址
"readCount": 16, //读取字的个数
"writeStart": 40016 //写入开始地址
} //可以写多个
],
"read": {
"type": "Tcp",
"address": "127.0.0.1:502", //读取的设备地址
"slaveAddress": 2, //从站地址
"masterAddress": 1 //主站地址
},
"write": {
"type": "Rtu",
"address": "COM2", //写入的设备地址
"slaveAddress": 3, //从站地址
"masterAddress": 1 //主站地址
}
}
}
}

View File

@@ -0,0 +1,4 @@
sc delete ModbusTcpToRtu
sc create ModbusTcpToRtu "binPath=%~dp0ModbusTcpToRtu.exe" start= delayed-auto
sc start ModbusTcpToRtu
pause

View File

@@ -0,0 +1,36 @@
using Quartz.Logging;
namespace SampleModbusRtuServer
{
// simple log provider to get something to the console
public class ConsoleLogProvider : ILogProvider
{
private readonly IConfigurationRoot configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ?? "Production"}.json", true)
.Build();
public Logger GetLogger(string name)
{
return (level, func, exception, parameters) =>
{
if (level >= configuration.GetSection("Quartz").GetValue<Quartz.Logging.LogLevel>("LogLevel") && func != null)
{
Console.WriteLine("[" + DateTime.Now.ToLongTimeString() + "] [" + level + "] " + func(), parameters);
}
return true;
};
}
public IDisposable OpenNestedContext(string message)
{
throw new NotImplementedException();
}
public IDisposable OpenMappedContext(string key, object value, bool destructure = false)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,34 @@
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MachineJob
{
public class DatabaseWriteContext : DbContext
{
private static readonly IConfigurationRoot configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.default.json", optional: false, reloadOnChange: true)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ?? Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", optional: true, reloadOnChange: true)
.Build();
private static readonly string connectionString = configuration.GetConnectionString("DatabaseWriteConnectionString")!;
public DbSet<DatabaseWriteEntity>? DatabaseWrites { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString));
}
}
[Table(name: "databasewrites")]
public partial class DatabaseWriteEntity
{
[Key]
public int Id { get; set; }
public DateTime UpdateTime { get; set; }
}
}

View File

@@ -0,0 +1,39 @@
using SampleModbusRtuServer;
using SampleModbusRtuServer.Service;
using Serilog;
IHost host = Host.CreateDefaultBuilder(args).UseWindowsService()
.ConfigureAppConfiguration((hostingContext, config) =>
{
var configuration = config
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ?? "Production"}.json", true)
.AddEnvironmentVariables()
.Build();
Directory.SetCurrentDirectory(hostingContext.HostingEnvironment.ContentRootPath);
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.File("Log\\log..txt", Serilog.Events.LogEventLevel.Error, shared: true, rollingInterval: RollingInterval.Day)
.CreateLogger();
var loggerFactory = new LoggerFactory().AddSerilog(Log.Logger);
Quartz.Logging.LogProvider.SetCurrentLogProvider(new ConsoleLogProvider());
Modbus.Net.LogProvider.SetLogProvider(loggerFactory);
}
)
.ConfigureServices(services =>
{
services.AddHostedService<Worker>();
services.AddLogging(loggingBuilder => loggingBuilder.AddSerilog(Log.Logger, true));
})
.Build();
await host.RunAsync();

View File

@@ -0,0 +1,58 @@
<Project Sdk="Microsoft.NET.Sdk.Worker">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>dotnet-SampleModbusRtuServer-b9a42287-9797-4b7f-81e6-0796c51ff8e0</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<_ContentIncludedByDefault Remove="appsettings.default.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.9">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<TreatAsUsed>true</TreatAsUsed>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="7.0.1" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="7.0.0" />
<PackageReference Include="Serilog.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="7.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Modbus.Net\Modbus.Net.Modbus\Modbus.Net.Modbus.csproj" />
<ProjectReference Include="..\..\Modbus.Net\Modbus.Net.Siemens\Modbus.Net.Siemens.csproj" />
<ProjectReference Include="..\..\Modbus.Net\Modbus.Net\Modbus.Net.csproj" />
<ProjectReference Include="..\MachineJob.CodeGenerator\MachineJob.CodeGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>
<ItemGroup>
<Content Update="appsettings.default.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Update="appsettings.Development.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Update="appsettings.Production.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,141 @@
using Modbus.Net;
using Modbus.Net.Modbus;
using MultipleMachinesJobScheduler = Modbus.Net.MultipleMachinesJobScheduler<Modbus.Net.IMachineMethodDatas, string, double>;
namespace SampleModbusRtuServer.Service
{
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
private bool[] zerox = new bool[10000];
private byte[] threex = new byte[20000];
private bool _isUpdate = false;
private DateTime _updateTime = DateTime.MinValue;
public Worker(ILogger<Worker> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
ModbusRtuProtocolReceiver receiver = new ModbusRtuProtocolReceiver("COM2", 1);
receiver.DataProcess = receiveContent =>
{
byte[]? returnBytes = null;
var readContent = new byte[receiveContent.Count * 2];
var values = receiveContent.WriteContent;
var valueDic = new Dictionary<string, double>();
var redisValues = new Dictionary<string, ReturnUnit<double>>();
if (values != null)
{
try
{
if (_isUpdate && DateTime.Now - _updateTime > TimeSpan.FromSeconds(9.5))
{
_logger.LogDebug($"receive content { String.Concat(receiveContent.WriteContent.Select(p => " " + p.ToString("X2")))}");
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error");
}
switch (receiveContent.FunctionCode)
{
case (byte)ModbusProtocolFunctionCode.WriteMultiRegister:
{
Array.Copy(receiveContent.WriteContent, 0, threex, receiveContent.StartAddress * 2, receiveContent.WriteContent.Length);
returnBytes = new WriteDataModbusProtocol().Format(receiveContent.SlaveAddress, receiveContent.FunctionCode, receiveContent.StartAddress, receiveContent.Count);
_isUpdate = true;
break;
}
case (byte)ModbusProtocolFunctionCode.WriteSingleCoil:
{
if (receiveContent.WriteContent[0] == 255)
{
zerox[receiveContent.StartAddress] = true;
}
else
{
zerox[receiveContent.StartAddress] = false;
}
returnBytes = new WriteDataModbusProtocol().Format(receiveContent.SlaveAddress, receiveContent.FunctionCode, receiveContent.StartAddress, receiveContent.WriteContent);
_isUpdate = true;
break;
}
case (byte)ModbusProtocolFunctionCode.WriteMultiCoil:
{
var pos = 0;
List<bool> bitList = new List<bool>();
for (int i = 0; i < receiveContent.WriteByteCount; i++)
{
var bitArray = BigEndianLsbValueHelper.Instance.GetBits(receiveContent.WriteContent, ref pos);
bitList.AddRange(bitArray.ToList());
}
Array.Copy(bitList.ToArray(), 0, zerox, receiveContent.StartAddress, bitList.Count);
returnBytes = new WriteDataModbusProtocol().Format(receiveContent.SlaveAddress, receiveContent.FunctionCode, receiveContent.StartAddress, receiveContent.Count);
_isUpdate = true;
break;
}
case (byte)ModbusProtocolFunctionCode.WriteSingleRegister:
{
Array.Copy(receiveContent.WriteContent, 0, threex, receiveContent.StartAddress * 2, receiveContent.Count * 2);
returnBytes = new WriteDataModbusProtocol().Format(receiveContent.SlaveAddress, receiveContent.FunctionCode, receiveContent.StartAddress, receiveContent.Count);
_isUpdate = true;
break;
}
}
}
else
{
switch (receiveContent.FunctionCode)
{
case (byte)ModbusProtocolFunctionCode.ReadHoldRegister:
{
Array.Copy(threex, receiveContent.StartAddress, readContent, 0, readContent.Length);
returnBytes = new ReadDataModbusProtocol().Format(receiveContent.SlaveAddress, receiveContent.FunctionCode, (byte)receiveContent.Count, readContent);
break;
}
case (byte)ModbusProtocolFunctionCode.ReadCoilStatus:
{
var bitCount = receiveContent.WriteByteCount * 8;
var boolContent = new bool[bitCount];
Array.Copy(zerox, receiveContent.StartAddress, boolContent, 0, bitCount);
var byteList = new List<byte>();
for (int i = 0; i < receiveContent.WriteByteCount; i++)
{
byte result = 0;
for (int j = i; j < i + 8; j++)
{
// 将布尔值转换为对应的位
byte bit = boolContent[j] ? (byte)1 : (byte)0;
// 使用左移位运算将位合并到结果字节中
result = (byte)((result << 1) | bit);
}
byteList.Add(result);
}
readContent = byteList.ToArray();
returnBytes = new ReadDataModbusProtocol().Format(receiveContent.SlaveAddress, receiveContent.FunctionCode, receiveContent.WriteByteCount, readContent);
break;
}
}
}
if (returnBytes != null) return returnBytes;
else return null;
};
await receiver.ConnectAsync();
}
public override Task StopAsync(CancellationToken cancellationToken)
{
return Task.Run(() => MultipleMachinesJobScheduler.CancelJob());
}
}
}

View File

@@ -0,0 +1,21 @@
{
"Serilog": {
"MinimumLevel": {
"Default": "Debug",
"Override": {
"Microsoft": "Debug",
"Microsoft.Hosting.Lifetime": "Debug"
}
}
},
"Logging": {
"LogLevel": {
"Default": "Debug",
"Microsoft": "Debug",
"Microsoft.Hosting.Lifetime": "Debug"
}
},
"Quartz": {
"LogLevel": "Debug"
}
}

View File

@@ -0,0 +1,21 @@
{
"Serilog": {
"MinimumLevel": {
"Default": "Error",
"Override": {
"Microsoft": "Error",
"Microsoft.Hosting.Lifetime": "Error"
}
}
},
"Logging": {
"LogLevel": {
"Default": "Error",
"Microsoft": "Error",
"Microsoft.Hosting.Lifetime": "Error"
}
},
"Quartz": {
"LogLevel": "Error"
}
}

Some files were not shown because too many files have changed in this diff Show More