Compare commits
21 Commits
master
...
e38c16e899
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e38c16e899 | ||
|
|
9cffd005a2 | ||
|
|
a6b467cc96 | ||
|
|
d0707a867a | ||
|
|
cc44c64dc4 | ||
|
|
4ecec7a35e | ||
|
|
34ba703696 | ||
|
|
258da627ac | ||
|
|
c6b5d9b928 | ||
|
|
0d65afd74f | ||
|
|
986bb9b561 | ||
|
|
d2594a3ff9 | ||
|
|
f7507428b8 | ||
|
|
9892eda959 | ||
|
|
e9a8705b03 | ||
|
|
b2bc6c82a9 | ||
|
|
d619bf36a1 | ||
|
|
d23b942464 | ||
|
|
511434d18a | ||
|
|
fac39b0bf1 | ||
|
|
3acd6aa1e0 |
318
.gitignore
vendored
318
.gitignore
vendored
@@ -1,34 +1,84 @@
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# Build results
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
build/
|
||||
x86/
|
||||
[Ww][Ii][Nn]32/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
[Ll]ogs/
|
||||
|
||||
# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
|
||||
!packages/*/build/
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.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
|
||||
[Tt]est[Rr]esult*/
|
||||
[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
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
@@ -38,26 +88,41 @@ build/
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.tlog
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.log
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
@@ -65,6 +130,7 @@ ipch/
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
@@ -72,9 +138,30 @@ _TeamCity*
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.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_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
@@ -93,66 +180,219 @@ DocProject/Help/html
|
||||
publish/
|
||||
|
||||
# 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
|
||||
## TODO: If you have NuGet Package Restore enabled, uncomment the next line
|
||||
#packages/
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# Windows Azure Build Output
|
||||
csx
|
||||
# NuGet Packages
|
||||
*.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
|
||||
|
||||
# Windows Store app package directory
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
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
|
||||
sql/
|
||||
*.Cache
|
||||
ClientBin/
|
||||
[Ss]tyle[Cc]op.*
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.[Pp]ublish.xml
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.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
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file to a newer
|
||||
# Visual Studio version. Backup files are not needed, because we have git ;-)
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
App_Data/*.mdf
|
||||
App_Data/*.ldf
|
||||
*.mdf
|
||||
*.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
|
||||
GeneratedArtifacts/
|
||||
_Pvt_Extensions/
|
||||
ModelManifest.xml
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# =========================
|
||||
# Windows detritus
|
||||
# =========================
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Windows image file caches
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Folder config file
|
||||
Desktop.ini
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Mac desktop service store files
|
||||
.DS_Store
|
||||
/Modbus.Net/packages
|
||||
/Modbus.Net/.vs
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# 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
|
||||
@@ -5,6 +5,8 @@ namespace Modbus.Net
|
||||
public partial class Endian
|
||||
{
|
||||
public const int BigEndian3412 = 10;
|
||||
|
||||
public const int LittleEndian3412 = 11;
|
||||
}
|
||||
|
||||
public class BigEndian3412ValueHelper : BigEndianLsbValueHelper
|
||||
@@ -36,6 +38,70 @@ namespace Modbus.Net
|
||||
public new static BigEndian3412ValueHelper Instance
|
||||
=> _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)
|
||||
{
|
||||
Array.Reverse(data, pos, 4);
|
||||
@@ -49,5 +115,137 @@ namespace Modbus.Net
|
||||
pos += 4;
|
||||
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>
|
||||
protected new bool LittleEndian => true;
|
||||
|
||||
protected new bool LittleEndianBit => false;
|
||||
|
||||
/// <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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.6.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace Modbus.Net.HJ212
|
||||
var sendValues = new List<Dictionary<string, string>>() { formatValues };
|
||||
//写入数据
|
||||
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)
|
||||
|
||||
@@ -16,7 +16,17 @@ namespace Modbus.Net.HJ212
|
||||
public HJ212Protocol(string ip)
|
||||
: 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>
|
||||
@@ -43,14 +53,15 @@ namespace Modbus.Net.HJ212
|
||||
public override byte[] Format(IInputStruct message)
|
||||
{
|
||||
var r_message = (WriteRequestHJ212InputStruct)message;
|
||||
string formatMessage = "##0633";
|
||||
string formatMessage = "";
|
||||
formatMessage += "QN=" + r_message.QN + ";";
|
||||
formatMessage += "ST=" + r_message.ST + ";";
|
||||
formatMessage += "CN=" + r_message.CN + ";";
|
||||
formatMessage += "PW=" + r_message.PW + ";";
|
||||
formatMessage += "MN=" + r_message.MN + ";";
|
||||
formatMessage += "Flag=5;";
|
||||
formatMessage += "CP=&&";
|
||||
formatMessage += "DateTime=" + r_message.Datetime.ToString("yyyyMMddHHmmss") + ";";
|
||||
formatMessage += "DataTime=" + r_message.Datatime + ";";
|
||||
foreach (var record in r_message.CP)
|
||||
{
|
||||
foreach (var data in record)
|
||||
@@ -61,7 +72,6 @@ namespace Modbus.Net.HJ212
|
||||
formatMessage += ";";
|
||||
}
|
||||
formatMessage = formatMessage[..^1];
|
||||
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)
|
||||
{
|
||||
QN = datetime.ToString("yyyyMMddHHmmssffff");
|
||||
ST = st;
|
||||
CN = cn;
|
||||
PW = pw;
|
||||
MN = mn;
|
||||
CP = cp;
|
||||
Datetime = datetime;
|
||||
Datatime = datetime.ToString("yyyyMMddHHmmss");
|
||||
}
|
||||
|
||||
public string QN => "20170101000926706";
|
||||
public string QN { get; }
|
||||
|
||||
public string ST { get; }
|
||||
|
||||
@@ -104,7 +115,7 @@ namespace Modbus.Net.HJ212
|
||||
|
||||
public List<Dictionary<string, string>> CP { get; }
|
||||
|
||||
public DateTime Datetime { get; }
|
||||
public string Datatime { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -15,14 +15,21 @@ namespace Modbus.Net.HJ212
|
||||
/// <returns>扩展后的协议内容</returns>
|
||||
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校验
|
||||
var newFormat = new byte[content.Length + 4];
|
||||
var crc = new byte[2];
|
||||
Crc16.GetInstance().GetCRC(content, ref crc);
|
||||
Array.Copy(content, 0, newFormat, 0, content.Length);
|
||||
string crcString = BitConverter.ToString(crc).Replace("-", string.Empty);
|
||||
crcString = "&&" + crcString + "\r\n";
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Modbus.Net.HJ212
|
||||
|
||||
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();
|
||||
}
|
||||
@@ -27,7 +27,7 @@ namespace Modbus.Net.HJ212
|
||||
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
|
||||
{
|
||||
|
||||
@@ -33,8 +33,7 @@ namespace Modbus.Net.Modbus
|
||||
/// <returns>校验是否正确</returns>
|
||||
public override bool? CheckRight(byte[] content)
|
||||
{
|
||||
//ProtocolLinker不会返回null
|
||||
if (base.CheckRight(content) != true) return false;
|
||||
if (base.CheckRight(content) != true) return base.CheckRight(content);
|
||||
//Modbus协议错误
|
||||
var contentString = Encoding.ASCII.GetString(content);
|
||||
if (byte.Parse(contentString.Substring(3, 2)) > 127)
|
||||
|
||||
@@ -33,8 +33,7 @@ namespace Modbus.Net.Modbus
|
||||
/// <returns>校验是否正确</returns>
|
||||
public override bool? CheckRight(byte[] content)
|
||||
{
|
||||
//ProtocolLinker不会返回null
|
||||
if (base.CheckRight(content) != true) return false;
|
||||
if (base.CheckRight(content) != true) return base.CheckRight(content);
|
||||
//Modbus协议错误
|
||||
var contentString = Encoding.ASCII.GetString(content);
|
||||
if (byte.Parse(contentString.Substring(3, 2)) > 127)
|
||||
|
||||
@@ -24,8 +24,7 @@ namespace Modbus.Net.Modbus
|
||||
/// <returns>校验是否正确</returns>
|
||||
public override bool? CheckRight(byte[] content)
|
||||
{
|
||||
//ProtocolLinker不会返回null
|
||||
if (base.CheckRight(content) != true) return false;
|
||||
if (base.CheckRight(content) != true) return base.CheckRight(content);
|
||||
//Modbus协议错误
|
||||
var contentString = Encoding.ASCII.GetString(content);
|
||||
if (byte.Parse(contentString.Substring(3, 2)) > 127)
|
||||
|
||||
@@ -23,9 +23,9 @@ namespace Modbus.Net.Modbus
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Modbus Rtu协议长度计算
|
||||
/// Modbus Rtu接收协议长度计算
|
||||
/// </summary>
|
||||
public static Func<byte[], int> ModbusRtuLengthCalc => content =>
|
||||
public static Func<byte[], int> ModbusRtuResponseLengthCalc => (content) =>
|
||||
{
|
||||
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;
|
||||
@@ -33,6 +33,20 @@ namespace Modbus.Net.Modbus
|
||||
else if (content[1] == 22) return 10;
|
||||
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>
|
||||
@@ -97,7 +111,7 @@ namespace Modbus.Net.Modbus
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modbus Rtu协议控制器
|
||||
/// Modbus Rtu发送协议控制器
|
||||
/// </summary>
|
||||
public class ModbusRtuController : FifoController
|
||||
{
|
||||
@@ -108,7 +122,7 @@ namespace Modbus.Net.Modbus
|
||||
/// <param name="slaveAddress">从站号</param>
|
||||
public ModbusRtuController(string com, int slaveAddress) : base(
|
||||
int.Parse(ConfigurationReader.GetValue("COM:" + com + ":" + slaveAddress, "FetchSleepTime")),
|
||||
lengthCalc: ModbusLengthCalc.ModbusRtuLengthCalc,
|
||||
lengthCalc: ModbusLengthCalc.ModbusRtuResponseLengthCalc,
|
||||
checkRightFunc: ContentCheck.Crc16CheckRight,
|
||||
waitingListMaxCount: ConfigurationReader.GetValue("COM:" + com + ":" + slaveAddress, "WaitingListCount") != null ?
|
||||
int.Parse(ConfigurationReader.GetValue("COM:" + com + ":" + slaveAddress, "WaitingListCount")) :
|
||||
@@ -118,7 +132,28 @@ namespace Modbus.Net.Modbus
|
||||
}
|
||||
|
||||
/// <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>
|
||||
public class ModbusRtuInTcpController : FifoController
|
||||
{
|
||||
@@ -129,7 +164,7 @@ namespace Modbus.Net.Modbus
|
||||
/// <param name="port">端口号</param>
|
||||
public ModbusRtuInTcpController(string ip, int port) : base(
|
||||
int.Parse(ConfigurationReader.GetValue("TCP:" + ip + ":" + port, "FetchSleepTime")),
|
||||
lengthCalc: ModbusLengthCalc.ModbusRtuLengthCalc,
|
||||
lengthCalc: ModbusLengthCalc.ModbusRtuResponseLengthCalc,
|
||||
checkRightFunc: ContentCheck.Crc16CheckRight,
|
||||
waitingListMaxCount: ConfigurationReader.GetValue("TCP:" + ip + ":" + port, "WaitingListCount") != null ?
|
||||
int.Parse(ConfigurationReader.GetValue("TCP:" + ip + ":" + port, "WaitingListCount")) :
|
||||
@@ -139,7 +174,28 @@ namespace Modbus.Net.Modbus
|
||||
}
|
||||
|
||||
/// <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>
|
||||
public class ModbusRtuInUdpController : FifoController
|
||||
{
|
||||
@@ -150,7 +206,28 @@ namespace Modbus.Net.Modbus
|
||||
/// <param name="port">端口号</param>
|
||||
public ModbusRtuInUdpController(string ip, int port) : base(
|
||||
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,
|
||||
waitingListMaxCount: ConfigurationReader.GetValue("UDP:" + ip + ":" + port, "WaitingListCount") != null ?
|
||||
int.Parse(ConfigurationReader.GetValue("UDP:" + ip + ":" + port, "WaitingListCount")) :
|
||||
|
||||
@@ -146,17 +146,18 @@ namespace Modbus.Net.Modbus
|
||||
/// </summary>
|
||||
/// <param name="slaveAddress">从站地址</param>
|
||||
/// <param name="startAddress">开始地址</param>
|
||||
/// <param name="getCount">读取个数</param>
|
||||
/// <param name="getByteCount">读取字节个数</param>
|
||||
/// <param name="getOriginalCount">读取原始个数</param>
|
||||
/// <param name="addressTranslator">地址翻译器</param>
|
||||
public ReadDataModbusInputStruct(byte slaveAddress, string startAddress, ushort getCount,
|
||||
AddressTranslator addressTranslator)
|
||||
public ReadDataModbusInputStruct(byte slaveAddress, string startAddress, ushort getByteCount,
|
||||
AddressTranslator addressTranslator, ushort getOriginalCount = 0)
|
||||
{
|
||||
SlaveAddress = slaveAddress;
|
||||
var translateAddress = addressTranslator.AddressTranslate(startAddress, true);
|
||||
FunctionCode = (byte)translateAddress.Area;
|
||||
StartAddress = (ushort)translateAddress.Address;
|
||||
GetCount =
|
||||
(ushort)Math.Ceiling(getCount / addressTranslator.GetAreaByteLength(translateAddress.AreaString));
|
||||
GetCount = translateAddress.AreaString == "0X" || translateAddress.AreaString == "1X" ? getOriginalCount :
|
||||
(ushort)Math.Ceiling(getByteCount / addressTranslator.GetAreaByteLength(translateAddress.AreaString));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -273,8 +274,9 @@ namespace Modbus.Net.Modbus
|
||||
/// <param name="writeValue">写入的数据</param>
|
||||
/// <param name="addressTranslator">地址翻译器</param>
|
||||
/// <param name="endian">端格式</param>
|
||||
/// <param name="setOriginalCount">设置原始长度</param>
|
||||
public WriteDataModbusInputStruct(byte slaveAddress, string startAddress, object[] writeValue,
|
||||
AddressTranslator addressTranslator, Endian endian)
|
||||
AddressTranslator addressTranslator, Endian endian, ushort setOriginalCount = 0)
|
||||
{
|
||||
SlaveAddress = slaveAddress;
|
||||
var translateAddress = addressTranslator.AddressTranslate(startAddress, false);
|
||||
@@ -282,7 +284,7 @@ namespace Modbus.Net.Modbus
|
||||
StartAddress = (ushort)translateAddress.Address;
|
||||
var writeByteValue = ValueHelper.GetInstance(endian).ObjectArrayToByteArray(writeValue);
|
||||
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));
|
||||
WriteByteCount = (byte)writeByteValue.Length;
|
||||
WriteValue = writeByteValue;
|
||||
|
||||
@@ -169,4 +169,9 @@ namespace Modbus.Net.Modbus
|
||||
return newContent.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public class ModbusRtuProtocolReceiverBytesExtend : ModbusRtuProtocolLinkerBytesExtend
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
200
Modbus.Net/Modbus.Net.Modbus/ModbusReceiver.cs
Normal file
200
Modbus.Net/Modbus.Net.Modbus/ModbusReceiver.cs
Normal file
@@ -0,0 +1,200 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
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 ModbusRtuProtocolReceiver _receiver;
|
||||
|
||||
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)
|
||||
{
|
||||
_receiver = new ModbusRtuProtocolReceiver(ConfigurationReader.GetValue("Receiver", "d:connectionString"), int.Parse(ConfigurationReader.GetValue("Receiver", "g:slaveAddress")));
|
||||
var machineName = ConfigurationReader.GetValue("Receiver", "a:id");
|
||||
var addressMapName = ConfigurationReader.GetValue("Receiver", "e:addressMap");
|
||||
var endian = ValueHelper.GetInstance(Endian.Parse(ConfigurationReader.GetValue("Receiver", "i: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 (ReturnValueDictionary != null)
|
||||
{
|
||||
var dataReturn = new DataReturnDef();
|
||||
dataReturn.MachineId = machineName;
|
||||
dataReturn.ReturnValues = new ReturnStruct<Dictionary<string, ReturnUnit<double>>>() { IsSuccess = true, Datas = returnDic };
|
||||
ReturnValueDictionary(dataReturn);
|
||||
}
|
||||
}
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<bool> ConnectAsync()
|
||||
{
|
||||
return await _receiver.ConnectAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,8 +31,7 @@
|
||||
/// <returns>数据是否正确</returns>
|
||||
public override bool? CheckRight(byte[] content)
|
||||
{
|
||||
//ProtocolLinker的CheckRight不会返回null
|
||||
if (base.CheckRight(content) != true) return false;
|
||||
if (base.CheckRight(content) != true) return base.CheckRight(content);
|
||||
//Modbus协议错误
|
||||
if (content[1] > 127)
|
||||
throw new ModbusProtocolErrorException(content[2]);
|
||||
|
||||
@@ -31,8 +31,7 @@
|
||||
/// <returns>数据是否正确</returns>
|
||||
public override bool? CheckRight(byte[] content)
|
||||
{
|
||||
//ProtocolLinker的CheckRight不会返回null
|
||||
if (base.CheckRight(content) != true) return false;
|
||||
if (base.CheckRight(content) != true) return base.CheckRight(content);
|
||||
//Modbus协议错误
|
||||
if (content[1] > 127)
|
||||
throw new ModbusProtocolErrorException(content[2]);
|
||||
|
||||
@@ -22,8 +22,7 @@
|
||||
/// <returns>数据是否正确</returns>
|
||||
public override bool? CheckRight(byte[] content)
|
||||
{
|
||||
//ProtocolLinker的CheckRight不会返回null
|
||||
if (base.CheckRight(content) != true) return false;
|
||||
if (base.CheckRight(content) != true) return base.CheckRight(content);
|
||||
//CRC校验失败
|
||||
if (!Crc16.GetInstance().CrcEfficacy(content))
|
||||
throw new ModbusProtocolErrorException(501);
|
||||
|
||||
67
Modbus.Net/Modbus.Net.Modbus/ModbusRtuProtocolReceiver.cs
Normal file
67
Modbus.Net/Modbus.Net.Modbus/ModbusRtuProtocolReceiver.cs
Normal 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;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,8 +30,7 @@
|
||||
/// <returns>数据是否正确</returns>
|
||||
public override bool? CheckRight(byte[] content)
|
||||
{
|
||||
//ProtocolLinker的CheckRight不会返回null
|
||||
if (base.CheckRight(content) != true) return false;
|
||||
if (base.CheckRight(content) != true) return base.CheckRight(content);
|
||||
//Modbus协议错误
|
||||
if (content[7] > 127)
|
||||
throw new ModbusProtocolErrorException(content[2] > 0 ? content[2] : content[8]);
|
||||
|
||||
@@ -30,8 +30,7 @@
|
||||
/// <returns>数据是否正确</returns>
|
||||
public override bool? CheckRight(byte[] content)
|
||||
{
|
||||
//ProtocolLinker的CheckRight不会返回null
|
||||
if (base.CheckRight(content) != true) return false;
|
||||
if (base.CheckRight(content) != true) return base.CheckRight(content);
|
||||
//Modbus协议错误
|
||||
if (content[7] > 127)
|
||||
throw new ModbusProtocolErrorException(content[2] > 0 ? content[2] : content[8]);
|
||||
|
||||
@@ -252,19 +252,19 @@ namespace Modbus.Net.Modbus
|
||||
}
|
||||
|
||||
/// <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
|
||||
{
|
||||
var inputStruct = new ReadDataModbusInputStruct(SlaveAddress, startAddress,
|
||||
(ushort)getByteCount, AddressTranslator);
|
||||
(ushort)getByteCount, AddressTranslator, (ushort)getOriginalCount);
|
||||
var outputStruct = await
|
||||
Wrapper.SendReceiveAsync<ReadDataModbusOutputStruct>(Wrapper[typeof(ReadDataModbusProtocol)],
|
||||
inputStruct);
|
||||
return new ReturnStruct<byte[]>
|
||||
{
|
||||
Datas = outputStruct?.DataValue,
|
||||
IsSuccess = true,
|
||||
IsSuccess = outputStruct == null ? null : true,
|
||||
ErrorCode = 0,
|
||||
ErrorMsg = ""
|
||||
};
|
||||
@@ -283,21 +283,22 @@ namespace Modbus.Net.Modbus
|
||||
}
|
||||
|
||||
/// <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
|
||||
{
|
||||
var inputStruct = new WriteDataModbusInputStruct(SlaveAddress, startAddress, setContents,
|
||||
AddressTranslator, Endian);
|
||||
AddressTranslator, Endian, (ushort)setOriginalCount);
|
||||
var outputStruct = await
|
||||
Wrapper.SendReceiveAsync<WriteDataModbusOutputStruct>(Wrapper[typeof(WriteDataModbusProtocol)],
|
||||
inputStruct);
|
||||
var ans = outputStruct?.WriteCount * 2 == BigEndianLsbValueHelper.Instance.ObjectArrayToByteArray(setContents).Length;
|
||||
return new ReturnStruct<bool>()
|
||||
{
|
||||
Datas = outputStruct?.WriteCount == setContents.Length,
|
||||
IsSuccess = outputStruct?.WriteCount == setContents.Length,
|
||||
ErrorCode = outputStruct?.WriteCount == setContents.Length ? 0 : -2,
|
||||
ErrorMsg = outputStruct?.WriteCount == setContents.Length ? "" : "Data length mismatch"
|
||||
Datas = ans,
|
||||
IsSuccess = ans,
|
||||
ErrorCode = ans ? 0 : -2,
|
||||
ErrorMsg = ans ? "" : "Data length mismatch"
|
||||
};
|
||||
}
|
||||
catch (ModbusProtocolErrorException e)
|
||||
@@ -325,7 +326,7 @@ namespace Modbus.Net.Modbus
|
||||
return new ReturnStruct<byte>()
|
||||
{
|
||||
Datas = outputStruct.OutputData,
|
||||
IsSuccess = true,
|
||||
IsSuccess = outputStruct == null ? null : true,
|
||||
ErrorCode = 0,
|
||||
ErrorMsg = null
|
||||
};
|
||||
@@ -355,7 +356,7 @@ namespace Modbus.Net.Modbus
|
||||
return new ReturnStruct<DiagnoticsData>()
|
||||
{
|
||||
Datas = new DiagnoticsData() { SubFunction = outputStruct.SubFunction, Data = outputStruct.Data },
|
||||
IsSuccess = true,
|
||||
IsSuccess = outputStruct == null ? null : true,
|
||||
ErrorCode = 0,
|
||||
ErrorMsg = null
|
||||
};
|
||||
@@ -385,7 +386,7 @@ namespace Modbus.Net.Modbus
|
||||
return new ReturnStruct<CommEventCounterData>()
|
||||
{
|
||||
Datas = new CommEventCounterData() { EventCount = outputStruct.EventCount, Status = outputStruct.Status },
|
||||
IsSuccess = true,
|
||||
IsSuccess = outputStruct == null ? null : true,
|
||||
ErrorCode = 0,
|
||||
ErrorMsg = null
|
||||
};
|
||||
@@ -415,7 +416,7 @@ namespace Modbus.Net.Modbus
|
||||
return new ReturnStruct<CommEventLogData>()
|
||||
{
|
||||
Datas = new CommEventLogData() { Status = outputStruct.Status, Events = outputStruct.Events },
|
||||
IsSuccess = true,
|
||||
IsSuccess = outputStruct == null ? null : true,
|
||||
ErrorCode = 0,
|
||||
ErrorMsg = null
|
||||
};
|
||||
@@ -445,7 +446,7 @@ namespace Modbus.Net.Modbus
|
||||
return new ReturnStruct<SlaveIdData>()
|
||||
{
|
||||
Datas = new SlaveIdData() { SlaveId = outputStruct.SlaveId, IndicatorStatus = outputStruct.RunIndicatorStatus, AdditionalData = outputStruct.AdditionalData },
|
||||
IsSuccess = true,
|
||||
IsSuccess = outputStruct == null ? null : true,
|
||||
ErrorCode = 0,
|
||||
ErrorMsg = null
|
||||
};
|
||||
@@ -475,7 +476,7 @@ namespace Modbus.Net.Modbus
|
||||
return new ReturnStruct<ReadFileRecordOutputDef[]>()
|
||||
{
|
||||
Datas = outputStruct.RecordDefs,
|
||||
IsSuccess = true,
|
||||
IsSuccess = outputStruct == null ? null : true,
|
||||
ErrorCode = 0,
|
||||
ErrorMsg = null
|
||||
};
|
||||
@@ -505,7 +506,7 @@ namespace Modbus.Net.Modbus
|
||||
return new ReturnStruct<WriteFileRecordOutputDef[]>()
|
||||
{
|
||||
Datas = outputStruct.WriteRecords,
|
||||
IsSuccess = true,
|
||||
IsSuccess = outputStruct == null ? null : true,
|
||||
ErrorCode = 0,
|
||||
ErrorMsg = null
|
||||
};
|
||||
@@ -535,7 +536,7 @@ namespace Modbus.Net.Modbus
|
||||
return new ReturnStruct<MaskRegisterData>()
|
||||
{
|
||||
Datas = new MaskRegisterData() { ReferenceAddress = outputStruct.ReferenceAddress, AndMask = outputStruct.AndMask, OrMask = outputStruct.OrMask },
|
||||
IsSuccess = true,
|
||||
IsSuccess = outputStruct == null ? null : true,
|
||||
ErrorCode = 0,
|
||||
ErrorMsg = null
|
||||
};
|
||||
@@ -565,7 +566,7 @@ namespace Modbus.Net.Modbus
|
||||
return new ReturnStruct<ushort[]>()
|
||||
{
|
||||
Datas = outputStruct.ReadRegisterValues,
|
||||
IsSuccess = true,
|
||||
IsSuccess = outputStruct == null ? null : true,
|
||||
ErrorCode = 0,
|
||||
ErrorMsg = null
|
||||
};
|
||||
@@ -595,7 +596,7 @@ namespace Modbus.Net.Modbus
|
||||
return new ReturnStruct<ushort[]>()
|
||||
{
|
||||
Datas = outputStruct.FIFOValueRegister,
|
||||
IsSuccess = true,
|
||||
IsSuccess = outputStruct == null ? null : true,
|
||||
ErrorCode = 0,
|
||||
ErrorMsg = null
|
||||
};
|
||||
|
||||
7
Modbus.Net/Modbus.Net.Modbus/ModbusUtilityServer.cs
Normal file
7
Modbus.Net/Modbus.Net.Modbus/ModbusUtilityServer.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Modbus.Net.Modbus
|
||||
{
|
||||
public class ModbusUtilityServer
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
Modbus.Net.Opc
|
||||
===================
|
||||
[](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.
|
||||
@@ -237,8 +237,9 @@ namespace Modbus.Net.Siemens
|
||||
/// </summary>
|
||||
/// <param name="startAddress">开始地址</param>
|
||||
/// <param name="getByteCount">读取字节个数</param>
|
||||
/// <param name="getOriginalCount">读取原始个数</param>
|
||||
/// <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
|
||||
{
|
||||
@@ -291,8 +292,9 @@ namespace Modbus.Net.Siemens
|
||||
/// </summary>
|
||||
/// <param name="startAddress">开始地址</param>
|
||||
/// <param name="setContents">需要写入的数据</param>
|
||||
/// <param name="setOriginalCount">写入数据的原始长度</param>
|
||||
/// <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
|
||||
{
|
||||
|
||||
@@ -27,29 +27,23 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Modbus.Net.Modbus.NA200H",
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AnyType", "..\Samples\AnyType\AnyType.csproj", "{1857DA63-3335-428F-84D8-1FA4F8178643}"
|
||||
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}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TripleAdd", "..\Samples\TripleAdd\TripleAdd.csproj", "{414956B8-DBD4-414C-ABD3-565580739646}"
|
||||
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}"
|
||||
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}"
|
||||
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}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Modbus.Net.CodeGenerator", "Modbus.Net.CodeGenerator\Modbus.Net.CodeGenerator.csproj", "{D3210531-BA79-49B2-9F99-09FD0E1627B6}"
|
||||
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
|
||||
Global
|
||||
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|x64.ActiveCfg = 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.Build.0 = 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|x64.ActiveCfg = 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.Build.0 = 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|x64.ActiveCfg = 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.Build.0 = 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|x64.ActiveCfg = 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
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -222,6 +192,8 @@ Global
|
||||
{AA3A42D2-0502-41D3-929A-BAB729DF07D6} = {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}
|
||||
{9CA7E35C-B5BC-4743-8732-1D8912AA12D8} = {3597B5C5-45B9-4ECB-92A3-D0FFBE47920A}
|
||||
{CB85D4B3-EEA2-431F-A47F-1DE7FB0A4802} = {3597B5C5-45B9-4ECB-92A3-D0FFBE47920A}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {AF00D64E-3C70-474A-8A81-E9E48017C4B5}
|
||||
|
||||
@@ -41,6 +41,20 @@ namespace Modbus.Net
|
||||
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>
|
||||
|
||||
@@ -22,15 +22,16 @@ namespace Modbus.Net
|
||||
/// <summary>
|
||||
/// 读取设备列表
|
||||
/// </summary>
|
||||
/// <param name="machineSection">读取设备的块名</param>
|
||||
/// <returns>设备的列表</returns>
|
||||
public static List<IMachine<string>> ReadMachines()
|
||||
public static List<IMachine<string>> ReadMachines(string machineSection = "Machine")
|
||||
{
|
||||
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)
|
||||
{
|
||||
List<KeyValuePair<string, string>> kv = new List<KeyValuePair<string, string>>();
|
||||
Dictionary<string, string> dic = new Dictionary<string, string>();
|
||||
var kv = new List<KeyValuePair<string, string>>();
|
||||
var dic = new Dictionary<string, string>();
|
||||
foreach (var paramO in machine.GetChildren())
|
||||
{
|
||||
foreach (var param in paramO.GetChildren())
|
||||
@@ -47,7 +48,10 @@ namespace Modbus.Net
|
||||
{
|
||||
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;
|
||||
}
|
||||
case "type":
|
||||
@@ -83,23 +87,7 @@ namespace Modbus.Net
|
||||
default:
|
||||
{
|
||||
string value = param.Value;
|
||||
bool boolValue;
|
||||
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);
|
||||
}
|
||||
paramsSet.Add(value);
|
||||
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[] typeParams = new Type[] { typeof(string), typeof(string) };
|
||||
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);
|
||||
}
|
||||
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()
|
||||
.SetBasePath(Directory.GetCurrentDirectory())
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Modbus.Net
|
||||
@@ -6,7 +7,7 @@ namespace Modbus.Net
|
||||
/// <inheritdoc />
|
||||
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>
|
||||
@@ -14,18 +15,10 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
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>
|
||||
public event MessageReturnDelegate MessageReturn;
|
||||
public Func<MessageReturnArgs<TParamOut>, MessageReturnCallbackArgs<TParamIn>> MessageReturn { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public void AddController(IController controller)
|
||||
@@ -76,7 +69,7 @@ namespace Modbus.Net
|
||||
/// <returns></returns>
|
||||
protected TParamIn InvokeReturnMessage(TParamOut receiveMessage)
|
||||
{
|
||||
return MessageReturn?.Invoke(this, new MessageReturnArgs<TParamOut> { ReturnMessage = receiveMessage })?.SendMessage;
|
||||
return MessageReturn?.Invoke(new MessageReturnArgs<TParamOut> { ReturnMessage = receiveMessage })?.SendMessage;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -212,7 +212,7 @@ namespace Modbus.Net
|
||||
/// <returns>串口实际读入数据个数 </returns>
|
||||
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);
|
||||
|
||||
if (SerialPort.IsOpen == false)
|
||||
@@ -278,7 +278,7 @@ namespace Modbus.Net
|
||||
}
|
||||
// Release unmanaged resources
|
||||
Controller?.SendStop();
|
||||
ReceiveMsgThreadStop();
|
||||
ReceiveMsgThreadStop();
|
||||
Linkers?.Remove((_slave, _com));
|
||||
logger.LogInformation("Com connector {ConnectionToken} Removed", _com);
|
||||
if (Linkers?.Count(p => p.Item2 == _com) == 0)
|
||||
@@ -288,12 +288,12 @@ namespace Modbus.Net
|
||||
SerialPort?.Close();
|
||||
}
|
||||
SerialPort?.Dispose();
|
||||
logger.LogInformation("Com interface {Com} Disposed", _com);
|
||||
logger.LogInformation("Com interface {Com} Disposed", _com);
|
||||
if (Connectors.ContainsKey(_com))
|
||||
{
|
||||
Connectors[_com] = null;
|
||||
Connectors.Remove(_com);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -500,7 +500,7 @@ namespace Modbus.Net
|
||||
if (_receiveThread == null)
|
||||
{
|
||||
_receiveThreadCancel = new CancellationTokenSource();
|
||||
_receiveThread = Task.Run(async ()=>await ReceiveMessage(_receiveThreadCancel.Token), _receiveThreadCancel.Token);
|
||||
_receiveThread = Task.Run(async () => await ReceiveMessage(_receiveThreadCancel.Token), _receiveThreadCancel.Token);
|
||||
try
|
||||
{
|
||||
await _receiveThread;
|
||||
|
||||
@@ -32,18 +32,10 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
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>
|
||||
public event MessageReturnDelegate MessageReturn;
|
||||
public Func<MessageReturnArgs<TParamOut>, MessageReturnCallbackArgs<TParamIn>> MessageReturn { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public void AddController(IController controller)
|
||||
@@ -84,7 +76,7 @@ namespace Modbus.Net
|
||||
/// <returns></returns>
|
||||
protected TParamIn InvokeReturnMessage(TParamOut receiveMessage)
|
||||
{
|
||||
return MessageReturn?.Invoke(this, new MessageReturnArgs<TParamOut> { ReturnMessage = receiveMessage })?.SendMessage;
|
||||
return MessageReturn?.Invoke(new MessageReturnArgs<TParamOut> { ReturnMessage = receiveMessage })?.SendMessage;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,7 +118,8 @@ namespace Modbus.Net
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -180,7 +180,8 @@ namespace Modbus.Net
|
||||
logger.LogDebug($"Udp client {ConnectionToken} send: {string.Concat(datagram.Select(p => " " + p.ToString("X2")))}");
|
||||
IByteBuffer buffer = Unpooled.Buffer();
|
||||
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);
|
||||
}
|
||||
catch (Exception err)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using Quartz.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -106,8 +105,8 @@ namespace Modbus.Net
|
||||
finally
|
||||
{
|
||||
_sendingThreadCancel.Dispose();
|
||||
_sendingThreadCancel = null;
|
||||
}
|
||||
_sendingThreadCancel = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
242
Modbus.Net/Modbus.Net/Controller/NoResponseController.cs
Normal file
242
Modbus.Net/Modbus.Net/Controller/NoResponseController.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,5 +36,34 @@ namespace Modbus.Net
|
||||
}
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Modbus.Net
|
||||
@@ -5,8 +6,13 @@ namespace Modbus.Net
|
||||
/// <summary>
|
||||
/// 基础的协议连接接口
|
||||
/// </summary>
|
||||
public interface IConnector<in TParamIn, TParamOut>
|
||||
public interface IConnector<TParamIn, TParamOut>
|
||||
{
|
||||
/// <summary>
|
||||
/// 数据返回代理
|
||||
/// </summary>
|
||||
Func<MessageReturnArgs<TParamOut>, MessageReturnCallbackArgs<TParamIn>> MessageReturn { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 标识Connector的连接关键字
|
||||
/// </summary>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/// <summary>
|
||||
/// 基础的协议连接接口
|
||||
/// </summary>
|
||||
public interface IConnectorWithController<in TParamIn, TParamOut> : IConnector<TParamIn, TParamOut>
|
||||
public interface IConnectorWithController<TParamIn, TParamOut> : IConnector<TParamIn, TParamOut>
|
||||
{
|
||||
/// <summary>
|
||||
/// 增加传输控制器
|
||||
|
||||
31
Modbus.Net/Modbus.Net/Interface/IMachineServerMethod.cs
Normal file
31
Modbus.Net/Modbus.Net/Interface/IMachineServerMethod.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
61
Modbus.Net/Modbus.Net/Interface/IProtocolReceiver.cs
Normal file
61
Modbus.Net/Modbus.Net/Interface/IProtocolReceiver.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -21,8 +21,9 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
/// <param name="startAddress">开始地址</param>
|
||||
/// <param name="getByteCount">获取字节数个数</param>
|
||||
/// <param name="getOriginalCount">获取原始个数</param>
|
||||
/// <returns>接收到的byte数据</returns>
|
||||
Task<ReturnStruct<byte[]>> GetDatasAsync(string startAddress, int getByteCount);
|
||||
Task<ReturnStruct<byte[]>> GetDatasAsync(string startAddress, int getByteCount, int getOriginalCount);
|
||||
|
||||
/// <summary>
|
||||
/// 获取数据
|
||||
@@ -53,7 +54,8 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
/// <param name="startAddress">开始地址</param>
|
||||
/// <param name="setContents">设置数据</param>
|
||||
/// <param name="setOriginalCount">设置原始长度</param>
|
||||
/// <returns>是否设置成功</returns>
|
||||
Task<ReturnStruct<bool>> SetDatasAsync(string startAddress, object[] setContents);
|
||||
Task<ReturnStruct<bool>> SetDatasAsync(string startAddress, object[] setContents, int setOriginalCount);
|
||||
}
|
||||
}
|
||||
9
Modbus.Net/Modbus.Net/Interface/IUtilityServer.cs
Normal file
9
Modbus.Net/Modbus.Net/Interface/IUtilityServer.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace Modbus.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// Api入口的抽象
|
||||
/// </summary>
|
||||
public interface IUtilityServer : IUtilityProperty, IUtilityServerMethodDatas
|
||||
{
|
||||
}
|
||||
}
|
||||
33
Modbus.Net/Modbus.Net/Interface/IUtilityServerMethod.cs
Normal file
33
Modbus.Net/Modbus.Net/Interface/IUtilityServerMethod.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,15 @@ namespace Modbus.Net
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
220
Modbus.Net/Modbus.Net/Linker/ProtocolReceiver.cs
Normal file
220
Modbus.Net/Modbus.Net/Linker/ProtocolReceiver.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,15 @@
|
||||
isFullDuplex = bool.Parse(isFullDuplex != null ? isFullDuplex.ToString() : null ?? ConfigurationReader.GetValue("TCP:" + ip + ":" + port, "FullDuplex"));
|
||||
//初始化连接对象
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,15 @@
|
||||
isFullDuplex = bool.Parse(isFullDuplex != null ? isFullDuplex.ToString() : null ?? ConfigurationReader.GetValue("UDP:" + ip + ":" + port, "FullDuplex"));
|
||||
//初始化连接对象
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -125,6 +125,7 @@ namespace Modbus.Net
|
||||
preNum - (int)Math.Floor(initNum),
|
||||
AddressTranslator.GetAreaByteLength(address.Area),
|
||||
ValueHelper.ByteLength[preType.FullName])),
|
||||
GetOriginalCount = groupedAddress.Count(),
|
||||
DataType = typeof(byte),
|
||||
OriginalAddresses = originalAddresses.ToList()
|
||||
});
|
||||
@@ -154,6 +155,7 @@ namespace Modbus.Net
|
||||
AddressHelper.MapProtocolGetCountToAbstractByteCount(
|
||||
preNum - (int)Math.Floor(initNum), AddressTranslator.GetAreaByteLength(area),
|
||||
ValueHelper.ByteLength[preType.FullName])),
|
||||
GetOriginalCount = groupedAddress.Count(),
|
||||
DataType = typeof(byte),
|
||||
OriginalAddresses = originalAddresses.ToList()
|
||||
});
|
||||
@@ -200,6 +202,7 @@ namespace Modbus.Net
|
||||
(int)
|
||||
Math.Ceiling(newByteCount /
|
||||
ValueHelper.ByteLength[communicationUnit.DataType.FullName]),
|
||||
GetOriginalCount = newOriginalAddresses.Count,
|
||||
OriginalAddresses = newOriginalAddresses
|
||||
};
|
||||
|
||||
@@ -215,6 +218,7 @@ namespace Modbus.Net
|
||||
(int)
|
||||
Math.Ceiling(oldByteCount /
|
||||
ValueHelper.ByteLength[communicationUnit.DataType.FullName]);
|
||||
communicationUnit.GetOriginalCount = oldOriginalAddresses.Count;
|
||||
communicationUnit.OriginalAddresses = oldOriginalAddresses;
|
||||
newAns.Add(communicationUnit);
|
||||
}
|
||||
@@ -244,6 +248,7 @@ namespace Modbus.Net
|
||||
SubAddress = address.SubAddress,
|
||||
DataType = address.DataType,
|
||||
GetCount = 1,
|
||||
GetOriginalCount = 1,
|
||||
OriginalAddresses = new List<AddressUnit<TKey, TAddressKey, TSubAddressKey>> { address }
|
||||
}).ToList();
|
||||
}
|
||||
@@ -345,6 +350,7 @@ namespace Modbus.Net
|
||||
orderedGap.GapNumber +
|
||||
(int)
|
||||
(nowAddress.GetCount * ValueHelper.ByteLength[nowAddress.DataType.FullName]),
|
||||
GetOriginalCount = preAddress.GetOriginalCount + nowAddress.GetOriginalCount + orderedGap.GapNumber,
|
||||
DataType = typeof(byte),
|
||||
OriginalAddresses = preAddress.OriginalAddresses.ToList().Union(nowAddress.OriginalAddresses)
|
||||
};
|
||||
|
||||
@@ -96,11 +96,22 @@ namespace Modbus.Net
|
||||
(int)
|
||||
Math.Ceiling(communicateAddress.GetCount *
|
||||
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>>>()
|
||||
{
|
||||
@@ -330,12 +341,21 @@ namespace Modbus.Net
|
||||
(int)
|
||||
Math.Ceiling(communicateAddress.GetCount *
|
||||
ValueHelper.ByteLength[
|
||||
communicateAddress.DataType.FullName]));
|
||||
communicateAddress.DataType.FullName]),
|
||||
communicateAddress.GetOriginalCount);
|
||||
|
||||
var valueHelper = ValueHelper.GetInstance(BaseUtility.Endian);
|
||||
//如果设备本身能获取到数据但是没有数据
|
||||
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)
|
||||
{
|
||||
@@ -439,7 +459,7 @@ namespace Modbus.Net
|
||||
await
|
||||
BaseUtility.GetUtilityMethods<IUtilityMethodDatas>().SetDatasAsync(addressStart,
|
||||
valueHelper.ByteArrayToObjectArray(datas.Datas,
|
||||
new KeyValuePair<Type, int>(communicateAddress.DataType, communicateAddress.GetCount)));
|
||||
new KeyValuePair<Type, int>(communicateAddress.DataType, communicateAddress.GetCount)), communicateAddress.GetOriginalCount);
|
||||
}
|
||||
//如果不保持连接,断开连接
|
||||
if (!KeepConnect)
|
||||
@@ -617,6 +637,9 @@ namespace Modbus.Net
|
||||
AddressFormater.FormatAddress(address.Area, address.Address,
|
||||
address.SubAddress),
|
||||
(int)
|
||||
Math.Ceiling(ValueHelper.ByteLength[
|
||||
address.DataType.FullName]),
|
||||
(int)
|
||||
Math.Ceiling(ValueHelper.ByteLength[
|
||||
address.DataType.FullName]));
|
||||
|
||||
@@ -876,7 +899,7 @@ namespace Modbus.Net
|
||||
//写入数据
|
||||
await
|
||||
BaseUtility.GetUtilityMethods<IUtilityMethodDatas>().SetDatasAsync(address,
|
||||
new object[] { data });
|
||||
new object[] { data } , 1);
|
||||
}
|
||||
//如果不保持连接,断开连接
|
||||
if (!KeepConnect)
|
||||
@@ -1006,6 +1029,11 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
public int GetCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取原始个数
|
||||
/// </summary>
|
||||
public int GetOriginalCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 数据类型
|
||||
/// </summary>
|
||||
|
||||
6
Modbus.Net/Modbus.Net/Machine/BaseServer.cs
Normal file
6
Modbus.Net/Modbus.Net/Machine/BaseServer.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Modbus.Net.Machine
|
||||
{
|
||||
public class BaseServer
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,7 @@
|
||||
<PackageReference Include="DotNetty.Handlers" Version="0.7.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.4" />
|
||||
<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="Quartz" Version="3.6.3" />
|
||||
<PackageReference Include="System.IO.Ports" Version="7.0.0" />
|
||||
|
||||
@@ -185,7 +185,12 @@ namespace Modbus.Net
|
||||
public virtual async Task<T> SendReceiveAsync<T>(TProtocolUnit unit, IInputStruct content)
|
||||
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>
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Modbus.Net
|
||||
/// <param name="parameters">传递给输入结构的参数</param>
|
||||
/// <param name="success">上次的管道是否成功执行</param>
|
||||
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>
|
||||
public async Task<PipeUnit> SendReceiveAsync(Endian endian, Func<byte[], object[]> inputStructCreator)
|
||||
{
|
||||
if (Success)
|
||||
if (Success == true)
|
||||
{
|
||||
var content = inputStructCreator.Invoke(ReturnParams);
|
||||
if (ProtocolLinker != null)
|
||||
@@ -47,7 +47,7 @@ namespace Modbus.Net
|
||||
await ProtocolLinker.SendReceiveAsync(ProtocolUnit<byte[], byte[]>.TranslateContent(endian, content)),
|
||||
true);
|
||||
}
|
||||
return new PipeUnit(ProtocolLinker, null, ReturnParams, false);
|
||||
return new PipeUnit(ProtocolLinker, null, ReturnParams, Success);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -62,8 +62,18 @@ namespace Modbus.Net
|
||||
{
|
||||
var receiveContent = await SendReceiveAsyncParamOut(unit, inputStructCreator);
|
||||
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,
|
||||
false);
|
||||
}
|
||||
@@ -106,7 +116,7 @@ namespace Modbus.Net
|
||||
/// <param name="protocolUnit">协议单元</param>
|
||||
/// <param name="parameters">输入参数</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;
|
||||
ProtocolUnit = protocolUnit;
|
||||
@@ -132,7 +142,7 @@ namespace Modbus.Net
|
||||
/// <summary>
|
||||
/// 本次管道是否成功
|
||||
/// </summary>
|
||||
public bool Success { get; }
|
||||
public bool? Success { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 向设备发送数据,返回输出参数
|
||||
@@ -143,7 +153,7 @@ namespace Modbus.Net
|
||||
protected async Task<TParamOut> SendReceiveAsyncParamOut(TProtocolUnit unit,
|
||||
Func<TParamOut, IInputStruct> inputStructCreator)
|
||||
{
|
||||
if (Success)
|
||||
if (Success == true)
|
||||
{
|
||||
var content = inputStructCreator.Invoke(ReturnParams);
|
||||
var formatContent = unit.Format(content);
|
||||
|
||||
@@ -61,7 +61,7 @@ Manage several types of Protocol to a same calling interface.
|
||||
|
||||
### 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
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
/// <summary>
|
||||
/// 操作是否成功
|
||||
/// </summary>
|
||||
public bool IsSuccess { get; set; }
|
||||
public bool? IsSuccess { get; set; }
|
||||
/// <summary>
|
||||
/// 错误代码
|
||||
/// </summary>
|
||||
|
||||
@@ -50,8 +50,9 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
/// <param name="startAddress">开始地址</param>
|
||||
/// <param name="getByteCount">获取字节数个数</param>
|
||||
/// <param name="getOriginalCount">获取原始个数</param>
|
||||
/// <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>
|
||||
/// 获取数据
|
||||
@@ -67,7 +68,7 @@ namespace Modbus.Net
|
||||
var typeName = getTypeAndCount.Key.FullName;
|
||||
var bCount = ValueHelper.ByteLength[typeName];
|
||||
var getReturnValue = await GetDatasAsync(startAddress,
|
||||
(int)Math.Ceiling(bCount * getTypeAndCount.Value));
|
||||
(int)Math.Ceiling(bCount * getTypeAndCount.Value), getTypeAndCount.Value);
|
||||
var getBytes = getReturnValue;
|
||||
if (getBytes.IsSuccess == false || getBytes.Datas == null)
|
||||
{
|
||||
@@ -105,15 +106,15 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
/// <typeparam name="T">需要接收的类型</typeparam>
|
||||
/// <param name="startAddress">开始地址</param>
|
||||
/// <param name="getByteCount">获取字节数个数</param>
|
||||
/// <param name="getCount">获取个数</param>
|
||||
/// <returns>接收到的对应的类型和数据</returns>
|
||||
public virtual async Task<ReturnStruct<T[]>> GetDatasAsync<T>(string startAddress,
|
||||
int getByteCount)
|
||||
int getCount)
|
||||
{
|
||||
try
|
||||
{
|
||||
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)
|
||||
{
|
||||
return new ReturnStruct<T[]>
|
||||
@@ -162,7 +163,7 @@ namespace Modbus.Net
|
||||
let typeName = getTypeAndCount.Key.FullName
|
||||
let bCount = ValueHelper.ByteLength[typeName]
|
||||
select (int)Math.Ceiling(bCount * getTypeAndCount.Value)).Sum();
|
||||
var getReturnValue = await GetDatasAsync(startAddress, bAllCount);
|
||||
var getReturnValue = await GetDatasAsync(startAddress, bAllCount, bAllCount);
|
||||
var getBytes = getReturnValue;
|
||||
if (getBytes.IsSuccess == false || getBytes.Datas == null)
|
||||
{
|
||||
@@ -200,8 +201,9 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
/// <param name="startAddress">开始地址</param>
|
||||
/// <param name="setContents">设置数据</param>
|
||||
/// <param name="setOriginalCount">设置原始长度</param>
|
||||
/// <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>
|
||||
/// 协议是否遵循小端格式
|
||||
|
||||
120
Modbus.Net/Modbus.Net/Utility/BaseUtilityServer.cs
Normal file
120
Modbus.Net/Modbus.Net/Utility/BaseUtilityServer.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,8 @@
|
||||
"Host": "opc.tcp://localhost/test"
|
||||
},
|
||||
"Controller": {
|
||||
"WaitingListCount": "100"
|
||||
"WaitingListCount": "100",
|
||||
"NoResponse": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
[Modbus.Net.Opc]( https://www.nuget.org/packages/Modbus.Net.Opc) Implements OPC DA and OPC UA protocol.
|
||||
|
||||
Supported Platforms
|
||||
-------------------
|
||||
* Visual Studio 2022
|
||||
@@ -31,4 +29,3 @@ Thanks
|
||||
* Quartz - Job Scheduler
|
||||
* Serilog - Logging
|
||||
* DotNetty - Network Transporting
|
||||
* h-opc & Technosoftware.DaAeHdaSolution & OPCFoundation.NetStandard - OPC Trasporting
|
||||
|
||||
@@ -46,7 +46,8 @@
|
||||
"Host": "opc.tcp://localhost/test"
|
||||
},
|
||||
"Controller": {
|
||||
"WaitingListCount": "100"
|
||||
"WaitingListCount": "100",
|
||||
"NoResponse": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,8 @@
|
||||
"Host": "opc.tcp://localhost/test"
|
||||
},
|
||||
"Controller": {
|
||||
"WaitingListCount": "100"
|
||||
"WaitingListCount": "100",
|
||||
"NoResponse": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.6.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
<ItemGroup>
|
||||
<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\Modbus.Net.csproj" />
|
||||
<ProjectReference Include="..\MachineJob.CodeGenerator\MachineJob.CodeGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
||||
|
||||
@@ -93,11 +93,11 @@ namespace MachineJob.Service
|
||||
private Dictionary<string, double>? QueryConsole(DataReturnDef dataReturnDef)
|
||||
{
|
||||
var values = dataReturnDef.ReturnValues.Datas;
|
||||
if (dataReturnDef.ReturnValues.IsSuccess)
|
||||
if (dataReturnDef.ReturnValues.IsSuccess == true)
|
||||
{
|
||||
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 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,8 @@
|
||||
"Host": "opc.tcp://localhost/test"
|
||||
},
|
||||
"Controller": {
|
||||
"WaitingListCount": "100"
|
||||
"WaitingListCount": "100",
|
||||
"NoResponse": false
|
||||
}
|
||||
}
|
||||
}
|
||||
36
Samples/ModbusTcpToRtu/ConsoleLogProvider.cs
Normal file
36
Samples/ModbusTcpToRtu/ConsoleLogProvider.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
33
Samples/ModbusTcpToRtu/ModbusTcpToRtu.csproj
Normal file
33
Samples/ModbusTcpToRtu/ModbusTcpToRtu.csproj
Normal 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>
|
||||
38
Samples/ModbusTcpToRtu/Program.cs
Normal file
38
Samples/ModbusTcpToRtu/Program.cs
Normal 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();
|
||||
|
||||
163
Samples/ModbusTcpToRtu/Worker.cs
Normal file
163
Samples/ModbusTcpToRtu/Worker.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
21
Samples/ModbusTcpToRtu/appsettings.Development.json
Normal file
21
Samples/ModbusTcpToRtu/appsettings.Development.json
Normal 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"
|
||||
}
|
||||
}
|
||||
21
Samples/ModbusTcpToRtu/appsettings.Production.json
Normal file
21
Samples/ModbusTcpToRtu/appsettings.Production.json
Normal 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"
|
||||
}
|
||||
}
|
||||
53
Samples/ModbusTcpToRtu/appsettings.default.json
Normal file
53
Samples/ModbusTcpToRtu/appsettings.default.json
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
55
Samples/ModbusTcpToRtu/appsettings.json
Normal file
55
Samples/ModbusTcpToRtu/appsettings.json
Normal 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 //主站地址
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
4
Samples/ModbusTcpToRtu/service.bat
Normal file
4
Samples/ModbusTcpToRtu/service.bat
Normal file
@@ -0,0 +1,4 @@
|
||||
sc delete ModbusTcpToRtu
|
||||
sc create ModbusTcpToRtu "binPath=%~dp0ModbusTcpToRtu.exe" start= delayed-auto
|
||||
sc start ModbusTcpToRtu
|
||||
pause
|
||||
36
Samples/SampleModbusRtuServer/ConsoleLogProvider.cs
Normal file
36
Samples/SampleModbusRtuServer/ConsoleLogProvider.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
34
Samples/SampleModbusRtuServer/DatabaseWrite.cs
Normal file
34
Samples/SampleModbusRtuServer/DatabaseWrite.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
39
Samples/SampleModbusRtuServer/Program.cs
Normal file
39
Samples/SampleModbusRtuServer/Program.cs
Normal 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();
|
||||
|
||||
58
Samples/SampleModbusRtuServer/SampleModbusRtuServer.csproj
Normal file
58
Samples/SampleModbusRtuServer/SampleModbusRtuServer.csproj
Normal 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>
|
||||
141
Samples/SampleModbusRtuServer/Worker.cs
Normal file
141
Samples/SampleModbusRtuServer/Worker.cs
Normal 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
21
Samples/SampleModbusRtuServer/appsettings.Development.json
Normal file
21
Samples/SampleModbusRtuServer/appsettings.Development.json
Normal 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"
|
||||
}
|
||||
}
|
||||
21
Samples/SampleModbusRtuServer/appsettings.Production.json
Normal file
21
Samples/SampleModbusRtuServer/appsettings.Production.json
Normal 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"
|
||||
}
|
||||
}
|
||||
53
Samples/SampleModbusRtuServer/appsettings.default.json
Normal file
53
Samples/SampleModbusRtuServer/appsettings.default.json
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
249
Samples/SampleModbusRtuServer/appsettings.json
Normal file
249
Samples/SampleModbusRtuServer/appsettings.json
Normal file
@@ -0,0 +1,249 @@
|
||||
{
|
||||
"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": {
|
||||
"Machine": [
|
||||
{
|
||||
"a:id": "ModbusMachine1",
|
||||
"b:protocol": "Modbus",
|
||||
"c:type": "Tcp",
|
||||
"d:connectionString": "127.0.0.1",
|
||||
"e:addressMap": "AddressMapModbus",
|
||||
"f:keepConnect": true,
|
||||
"g:slaveAddress": 1,
|
||||
"h:masterAddress": 2,
|
||||
"i:endian": "BigEndianLsb"
|
||||
},
|
||||
{
|
||||
"a:id": "SiemensMachine1",
|
||||
"b:protocol": "Siemens",
|
||||
"c:type": "Tcp",
|
||||
"d:connectionString": "127.0.0.1",
|
||||
"e:model": "S7_1200",
|
||||
"f:addressMap": "AddressMapSiemens",
|
||||
"g:keepConnect": true,
|
||||
"h:slaveAddress": 1,
|
||||
"i:masterAddress": 2,
|
||||
"j:src": 1,
|
||||
"k:dst": 0
|
||||
},
|
||||
{
|
||||
"a:id": "ModbusMachine2",
|
||||
"b:protocol": "Modbus",
|
||||
"c:type": "Rtu",
|
||||
"d:connectionString": "COM1",
|
||||
"e:addressMap": "AddressMapModbus",
|
||||
"f:keepConnect": true,
|
||||
"g:slaveAddress": 1,
|
||||
"h:masterAddress": 2,
|
||||
"i:endian": "BigEndianLsb"
|
||||
},
|
||||
{
|
||||
"a:id": "SiemensMachine2",
|
||||
"b:protocol": "Siemens",
|
||||
"c:type": "Ppi",
|
||||
"d:connectionString": "COM2",
|
||||
"e:model": "S7_200",
|
||||
"f:addressMap": "AddressMapSiemens",
|
||||
"g:keepConnect": true,
|
||||
"h:slaveAddress": 2,
|
||||
"i:masterAddress": 0,
|
||||
"j:src": 1,
|
||||
"k:dst": 0
|
||||
},
|
||||
{
|
||||
"a:id": "OpcMachine1",
|
||||
"b:protocol": "Opc",
|
||||
"c:type": "Da",
|
||||
"d:connectionString": "opcda://localhost/Matrikon.OPC.Simulation.1",
|
||||
"e:addressMap": "AddressMapOpc",
|
||||
"f:tagSpliter": "."
|
||||
}
|
||||
],
|
||||
"addressMap": {
|
||||
"AddressMapModbus": [
|
||||
{
|
||||
"Area": "4X",
|
||||
"Address": 1,
|
||||
"DataType": "Int16",
|
||||
"Id": "1",
|
||||
"Name": "Test1"
|
||||
},
|
||||
{
|
||||
"Area": "4X",
|
||||
"Address": 2,
|
||||
"DataType": "Int16",
|
||||
"Id": "2",
|
||||
"Name": "Test2"
|
||||
},
|
||||
{
|
||||
"Area": "4X",
|
||||
"Address": 3,
|
||||
"DataType": "Int16",
|
||||
"Id": "3",
|
||||
"Name": "Test3"
|
||||
},
|
||||
{
|
||||
"Area": "4X",
|
||||
"Address": 4,
|
||||
"DataType": "Int16",
|
||||
"Id": "4",
|
||||
"Name": "Test4"
|
||||
},
|
||||
{
|
||||
"Area": "4X",
|
||||
"Address": 5,
|
||||
"DataType": "Int16",
|
||||
"Id": "5",
|
||||
"Name": "Test5"
|
||||
},
|
||||
{
|
||||
"Area": "4X",
|
||||
"Address": 6,
|
||||
"DataType": "Int16",
|
||||
"Id": "6",
|
||||
"Name": "Test6"
|
||||
},
|
||||
{
|
||||
"Area": "4X",
|
||||
"Address": 7,
|
||||
"DataType": "Int16",
|
||||
"Id": "7",
|
||||
"Name": "Test7"
|
||||
},
|
||||
{
|
||||
"Area": "4X",
|
||||
"Address": 8,
|
||||
"DataType": "Int16",
|
||||
"Id": "8",
|
||||
"Name": "Test8"
|
||||
},
|
||||
{
|
||||
"Area": "4X",
|
||||
"Address": 9,
|
||||
"DataType": "Int16",
|
||||
"Id": "9",
|
||||
"Name": "Test9"
|
||||
},
|
||||
{
|
||||
"Area": "4X",
|
||||
"Address": 10,
|
||||
"DataType": "Int16",
|
||||
"Id": "10",
|
||||
"Name": "Test10"
|
||||
}
|
||||
],
|
||||
"AddressMapSiemens": [
|
||||
{
|
||||
"Area": "DB1",
|
||||
"Address": 0,
|
||||
"DataType": "Int16",
|
||||
"Id": "1",
|
||||
"Name": "Test1"
|
||||
},
|
||||
{
|
||||
"Area": "DB1",
|
||||
"Address": 2,
|
||||
"DataType": "Int16",
|
||||
"Id": "2",
|
||||
"Name": "Test2"
|
||||
},
|
||||
{
|
||||
"Area": "DB1",
|
||||
"Address": 4,
|
||||
"DataType": "Int16",
|
||||
"Id": "3",
|
||||
"Name": "Test3"
|
||||
},
|
||||
{
|
||||
"Area": "DB1",
|
||||
"Address": 6,
|
||||
"DataType": "Int16",
|
||||
"Id": "4",
|
||||
"Name": "Test4"
|
||||
},
|
||||
{
|
||||
"Area": "DB1",
|
||||
"Address": 8,
|
||||
"DataType": "Int16",
|
||||
"Id": "5",
|
||||
"Name": "Test5"
|
||||
},
|
||||
{
|
||||
"Area": "DB1",
|
||||
"Address": 10,
|
||||
"DataType": "Int16",
|
||||
"Id": "6",
|
||||
"Name": "Test6"
|
||||
},
|
||||
{
|
||||
"Area": "DB1",
|
||||
"Address": 12,
|
||||
"DataType": "Int16",
|
||||
"Id": "7",
|
||||
"Name": "Test7"
|
||||
},
|
||||
{
|
||||
"Area": "DB1",
|
||||
"Address": 14,
|
||||
"DataType": "Int16",
|
||||
"Id": "8",
|
||||
"Name": "Test8"
|
||||
},
|
||||
{
|
||||
"Area": "DB1",
|
||||
"Address": 16,
|
||||
"DataType": "Int16",
|
||||
"Id": "9",
|
||||
"Name": "Test9"
|
||||
},
|
||||
{
|
||||
"Area": "DB1",
|
||||
"Address": 18,
|
||||
"DataType": "Int16",
|
||||
"Id": "10",
|
||||
"Name": "Test10"
|
||||
}
|
||||
],
|
||||
"AddressMapOpc": [
|
||||
{
|
||||
"Area": "Random",
|
||||
"Address": "Real4",
|
||||
"DataType": "Single",
|
||||
"Id": "1",
|
||||
"Name": "Test1",
|
||||
"DecimalPos": 2
|
||||
},
|
||||
{
|
||||
"Area": "Random",
|
||||
"Address": "Real8",
|
||||
"DataType": "Double",
|
||||
"Id": "2",
|
||||
"Name": "Test2",
|
||||
"DecimalPos": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -84,7 +84,7 @@ namespace TripleAdd.Controllers
|
||||
public async Task<ActionResult> SetUtility(TripleAddViewModel model)
|
||||
{
|
||||
ushort add1 = model.Add1, add2 = model.Add2, add3 = model.Add3;
|
||||
await utility!.SetDatasAsync("4X 1", new object[] { add1, add2, add3 });
|
||||
await utility!.SetDatasAsync("4X 1", new object[] { add1, add2, add3 }, 3);
|
||||
return RedirectToAction("Utility");
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,8 @@
|
||||
"Host": "opc.tcp://localhost/test"
|
||||
},
|
||||
"Controller": {
|
||||
"WaitingListCount": "100"
|
||||
"WaitingListCount": "100",
|
||||
"NoResponse": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,876 +0,0 @@
|
||||
#region Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
// Web: https://www.technosoftware.com
|
||||
//
|
||||
// The source code in this file is covered under a dual-license scenario:
|
||||
// - Owner of a purchased license: SCLA 1.0
|
||||
// - GPL V3: everybody else
|
||||
//
|
||||
// SCLA license terms accompanied with this source code.
|
||||
// See SCLA 1.0: https://technosoftware.com/license/Source_Code_License_Agreement.pdf
|
||||
//
|
||||
// GNU General Public License as published by the Free Software Foundation;
|
||||
// version 3 of the License are accompanied with this source code.
|
||||
// See https://technosoftware.com/license/GPLv3License.txt
|
||||
//
|
||||
// This source code is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//-----------------------------------------------------------------------------
|
||||
#endregion Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
|
||||
#region Using Directives
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using Technosoftware.DaAeHdaClient.Ae;
|
||||
using Technosoftware.DaAeHdaClient.Da;
|
||||
#endregion
|
||||
|
||||
#pragma warning disable 0618
|
||||
|
||||
namespace Technosoftware.DaAeHdaClient.Com.Ae
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines COM marshalling/unmarshalling functions for AE.
|
||||
/// </summary>
|
||||
internal class Interop
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a standard FILETIME to an OpcRcw.Ae.FILETIME structure.
|
||||
/// </summary>
|
||||
internal static OpcRcw.Ae.FILETIME Convert(FILETIME input)
|
||||
{
|
||||
var output = new OpcRcw.Ae.FILETIME();
|
||||
output.dwLowDateTime = input.dwLowDateTime;
|
||||
output.dwHighDateTime = input.dwHighDateTime;
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an OpcRcw.Ae.FILETIME to a standard FILETIME structure.
|
||||
/// </summary>
|
||||
internal static FILETIME Convert(OpcRcw.Ae.FILETIME input)
|
||||
{
|
||||
var output = new FILETIME();
|
||||
output.dwLowDateTime = input.dwLowDateTime;
|
||||
output.dwHighDateTime = input.dwHighDateTime;
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the HRESULT to a system type.
|
||||
/// </summary>
|
||||
internal static OpcResult GetResultID(int input)
|
||||
{
|
||||
// must check for this error because of a code collision with a DA code.
|
||||
if (input == Result.E_INVALIDBRANCHNAME)
|
||||
{
|
||||
return OpcResult.Ae.E_INVALIDBRANCHNAME;
|
||||
}
|
||||
|
||||
return Technosoftware.DaAeHdaClient.Com.Interop.GetResultID(input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unmarshals and deallocates a OPCEVENTSERVERSTATUS structure.
|
||||
/// </summary>
|
||||
internal static OpcServerStatus GetServerStatus(ref IntPtr pInput, bool deallocate)
|
||||
{
|
||||
OpcServerStatus output = null;
|
||||
|
||||
if (pInput != IntPtr.Zero)
|
||||
{
|
||||
var status = (OpcRcw.Ae.OPCEVENTSERVERSTATUS)Marshal.PtrToStructure(pInput, typeof(OpcRcw.Ae.OPCEVENTSERVERSTATUS));
|
||||
|
||||
output = new OpcServerStatus();
|
||||
|
||||
output.VendorInfo = status.szVendorInfo;
|
||||
output.ProductVersion = string.Format("{0}.{1}.{2}", status.wMajorVersion, status.wMinorVersion, status.wBuildNumber);
|
||||
output.MajorVersion = status.wMajorVersion;
|
||||
output.MinorVersion = status.wMinorVersion;
|
||||
output.BuildNumber = status.wBuildNumber;
|
||||
|
||||
output.ServerState = (OpcServerState)status.dwServerState;
|
||||
output.StatusInfo = null;
|
||||
output.StartTime = Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(Convert(status.ftStartTime));
|
||||
output.CurrentTime = Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(Convert(status.ftCurrentTime));
|
||||
output.LastUpdateTime = Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(Convert(status.ftLastUpdateTime));
|
||||
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.DestroyStructure(pInput, typeof(OpcRcw.Ae.OPCEVENTSERVERSTATUS));
|
||||
Marshal.FreeCoTaskMem(pInput);
|
||||
pInput = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a NodeType value to the OPCAEBROWSETYPE equivalent.
|
||||
/// </summary>
|
||||
internal static OpcRcw.Ae.OPCAEBROWSETYPE GetBrowseType(TsCAeBrowseType input)
|
||||
{
|
||||
switch (input)
|
||||
{
|
||||
case TsCAeBrowseType.Area: return OpcRcw.Ae.OPCAEBROWSETYPE.OPC_AREA;
|
||||
case TsCAeBrowseType.Source: return OpcRcw.Ae.OPCAEBROWSETYPE.OPC_SOURCE;
|
||||
}
|
||||
|
||||
return OpcRcw.Ae.OPCAEBROWSETYPE.OPC_AREA;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an array of ONEVENTSTRUCT structs to an array of EventNotification objects.
|
||||
/// </summary>
|
||||
internal static TsCAeEventNotification[] GetEventNotifications(OpcRcw.Ae.ONEVENTSTRUCT[] input)
|
||||
{
|
||||
TsCAeEventNotification[] output = null;
|
||||
|
||||
if (input != null && input.Length > 0)
|
||||
{
|
||||
output = new TsCAeEventNotification[input.Length];
|
||||
|
||||
for (var ii = 0; ii < input.Length; ii++)
|
||||
{
|
||||
output[ii] = GetEventNotification(input[ii]);
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a ONEVENTSTRUCT struct to a EventNotification object.
|
||||
/// </summary>
|
||||
internal static TsCAeEventNotification GetEventNotification(OpcRcw.Ae.ONEVENTSTRUCT input)
|
||||
{
|
||||
var output = new TsCAeEventNotification();
|
||||
|
||||
output.SourceID = input.szSource;
|
||||
output.Time = Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(Convert(input.ftTime));
|
||||
output.Severity = input.dwSeverity;
|
||||
output.Message = input.szMessage;
|
||||
output.EventType = (TsCAeEventType)input.dwEventType;
|
||||
output.EventCategory = input.dwEventCategory;
|
||||
output.ChangeMask = input.wChangeMask;
|
||||
output.NewState = input.wNewState;
|
||||
output.Quality = new TsCDaQuality(input.wQuality);
|
||||
output.ConditionName = input.szConditionName;
|
||||
output.SubConditionName = input.szSubconditionName;
|
||||
output.AckRequired = input.bAckRequired != 0;
|
||||
output.ActiveTime = Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(Convert(input.ftActiveTime));
|
||||
output.Cookie = input.dwCookie;
|
||||
output.ActorID = input.szActorID;
|
||||
|
||||
var attributes = Technosoftware.DaAeHdaClient.Com.Interop.GetVARIANTs(ref input.pEventAttributes, input.dwNumEventAttrs, false);
|
||||
|
||||
output.SetAttributes(attributes);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an array of OPCCONDITIONSTATE structs to an array of Condition objects.
|
||||
/// </summary>
|
||||
internal static TsCAeCondition[] GetConditions(ref IntPtr pInput, int count, bool deallocate)
|
||||
{
|
||||
TsCAeCondition[] output = null;
|
||||
|
||||
if (pInput != IntPtr.Zero && count > 0)
|
||||
{
|
||||
output = new TsCAeCondition[count];
|
||||
|
||||
var pos = pInput;
|
||||
|
||||
for (var ii = 0; ii < count; ii++)
|
||||
{
|
||||
var condition = (OpcRcw.Ae.OPCCONDITIONSTATE)Marshal.PtrToStructure(pos, typeof(OpcRcw.Ae.OPCCONDITIONSTATE));
|
||||
|
||||
output[ii] = new TsCAeCondition();
|
||||
|
||||
output[ii].State = condition.wState;
|
||||
output[ii].Quality = new TsCDaQuality(condition.wQuality);
|
||||
output[ii].Comment = condition.szComment;
|
||||
output[ii].AcknowledgerID = condition.szAcknowledgerID;
|
||||
output[ii].CondLastActive = Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(Convert(condition.ftCondLastActive));
|
||||
output[ii].CondLastInactive = Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(Convert(condition.ftCondLastInactive));
|
||||
output[ii].SubCondLastActive = Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(Convert(condition.ftSubCondLastActive));
|
||||
output[ii].LastAckTime = Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(Convert(condition.ftLastAckTime));
|
||||
|
||||
output[ii].ActiveSubCondition.Name = condition.szActiveSubCondition;
|
||||
output[ii].ActiveSubCondition.Definition = condition.szASCDefinition;
|
||||
output[ii].ActiveSubCondition.Severity = condition.dwASCSeverity;
|
||||
output[ii].ActiveSubCondition.Description = condition.szASCDescription;
|
||||
|
||||
// unmarshal sub-conditions.
|
||||
var names = Technosoftware.DaAeHdaClient.Com.Interop.GetUnicodeStrings(ref condition.pszSCNames, condition.dwNumSCs, deallocate);
|
||||
var severities = Technosoftware.DaAeHdaClient.Com.Interop.GetInt32s(ref condition.pdwSCSeverities, condition.dwNumSCs, deallocate);
|
||||
var definitions = Technosoftware.DaAeHdaClient.Com.Interop.GetUnicodeStrings(ref condition.pszSCDefinitions, condition.dwNumSCs, deallocate);
|
||||
var descriptions = Technosoftware.DaAeHdaClient.Com.Interop.GetUnicodeStrings(ref condition.pszSCDescriptions, condition.dwNumSCs, deallocate);
|
||||
|
||||
output[ii].SubConditions.Clear();
|
||||
|
||||
if (condition.dwNumSCs > 0)
|
||||
{
|
||||
for (var jj = 0; jj < names.Length; jj++)
|
||||
{
|
||||
var subcondition = new TsCAeSubCondition();
|
||||
|
||||
subcondition.Name = names[jj];
|
||||
subcondition.Severity = severities[jj];
|
||||
subcondition.Definition = definitions[jj];
|
||||
subcondition.Description = descriptions[jj];
|
||||
|
||||
output[ii].SubConditions.Add(subcondition);
|
||||
}
|
||||
}
|
||||
|
||||
// unmarshal attributes.
|
||||
var values = Technosoftware.DaAeHdaClient.Com.Interop.GetVARIANTs(ref condition.pEventAttributes, condition.dwNumEventAttrs, deallocate);
|
||||
var errors = Technosoftware.DaAeHdaClient.Com.Interop.GetInt32s(ref condition.pErrors, condition.dwNumEventAttrs, deallocate);
|
||||
|
||||
output[ii].Attributes.Clear();
|
||||
|
||||
if (condition.dwNumEventAttrs > 0)
|
||||
{
|
||||
for (var jj = 0; jj < values.Length; jj++)
|
||||
{
|
||||
var attribute = new TsCAeAttributeValue();
|
||||
|
||||
attribute.ID = 0;
|
||||
attribute.Value = values[jj];
|
||||
attribute.Result = GetResultID(errors[jj]);
|
||||
|
||||
output[ii].Attributes.Add(attribute);
|
||||
}
|
||||
}
|
||||
|
||||
// deallocate structure.
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.DestroyStructure(pos, typeof(OpcRcw.Ae.OPCCONDITIONSTATE));
|
||||
}
|
||||
|
||||
pos = (IntPtr)(pos.ToInt64() + Marshal.SizeOf(typeof(OpcRcw.Ae.OPCCONDITIONSTATE)));
|
||||
}
|
||||
|
||||
// deallocate array.
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.FreeCoTaskMem(pInput);
|
||||
pInput = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/*
|
||||
/// <summary>
|
||||
/// Converts an array of COM HRESULTs structures to .NET ResultID objects.
|
||||
/// </summary>
|
||||
internal static ResultID[] GetResultIDs(ref IntPtr pInput, int count, bool deallocate)
|
||||
{
|
||||
ResultID[] output = null;
|
||||
|
||||
if (pInput != IntPtr.Zero && count > 0)
|
||||
{
|
||||
output = new ResultID[count];
|
||||
|
||||
int[] errors = OpcCom.Interop.GetInt32s(ref pInput, count, deallocate);
|
||||
|
||||
for (int ii = 0; ii < count; ii++)
|
||||
{
|
||||
output[ii] = OpcCom.Interop.GetResultID(errors[ii]);
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an array of COM SourceServer structures to .NET SourceServer objects.
|
||||
/// </summary>
|
||||
internal static SourceServer[] GetSourceServers(ref IntPtr pInput, int count, bool deallocate)
|
||||
{
|
||||
SourceServer[] output = null;
|
||||
|
||||
if (pInput != IntPtr.Zero && count > 0)
|
||||
{
|
||||
output = new SourceServer[count];
|
||||
|
||||
IntPtr pos = pInput;
|
||||
|
||||
for (int ii = 0; ii < count; ii++)
|
||||
{
|
||||
OpcRcw.Dx.SourceServer server = (OpcRcw.Dx.SourceServer)Marshal.PtrToStructure(pos, typeof(OpcRcw.Dx.SourceServer));
|
||||
|
||||
output[ii] = new SourceServer();
|
||||
|
||||
output[ii].ItemName = server.szItemName;
|
||||
output[ii].ItemPath = server.szItemPath;
|
||||
output[ii].Version = server.szVersion;
|
||||
output[ii].Name = server.szName;
|
||||
output[ii].Description = server.szDescription;
|
||||
output[ii].ServerType = server.szServerType;
|
||||
output[ii].ServerURL = server.szServerURL;
|
||||
output[ii].DefaultConnected = server.bDefaultSourceServerConnected != 0;
|
||||
|
||||
pos = (IntPtr)(pos.ToInt64() + Marshal.SizeOf(typeof(OpcRcw.Dx.SourceServer)));
|
||||
}
|
||||
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.FreeCoTaskMem(pInput);
|
||||
pInput = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an array of .NET SourceServer objects to COM SourceServer structures.
|
||||
/// </summary>
|
||||
internal static OpcRcw.Dx.SourceServer[] GetSourceServers(SourceServer[] input)
|
||||
{
|
||||
OpcRcw.Dx.SourceServer[] output = null;
|
||||
|
||||
if (input != null && input.Length > 0)
|
||||
{
|
||||
output = new OpcRcw.Dx.SourceServer[input.Length];
|
||||
|
||||
for (int ii = 0; ii < input.Length; ii++)
|
||||
{
|
||||
output[ii] = new OpcRcw.Dx.SourceServer();
|
||||
|
||||
output[ii].dwMask = (uint)OpcRcw.Dx.Mask.All;
|
||||
output[ii].szItemName = input[ii].ItemName;
|
||||
output[ii].szItemPath = input[ii].ItemPath;
|
||||
output[ii].szVersion = input[ii].Version;
|
||||
output[ii].szName = input[ii].Name;
|
||||
output[ii].szDescription = input[ii].Description;
|
||||
output[ii].szServerType = input[ii].ServerType;
|
||||
output[ii].szServerURL = input[ii].ServerURL;
|
||||
output[ii].bDefaultSourceServerConnected = (input[ii].DefaultConnected)?1:0;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an array of COM DXGeneralResponse structure to a .NET GeneralResponse object.
|
||||
/// </summary>
|
||||
internal static GeneralResponse GetGeneralResponse(OpcRcw.Dx.DXGeneralResponse input, bool deallocate)
|
||||
{
|
||||
Opc.Dx.IdentifiedResult[] results = Interop.GetIdentifiedResults(ref input.pIdentifiedResults, input.dwCount, deallocate);
|
||||
|
||||
return new GeneralResponse(input.szConfigurationVersion, results);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an array of COM IdentifiedResult structures to .NET IdentifiedResult objects.
|
||||
/// </summary>
|
||||
internal static Opc.Dx.IdentifiedResult[] GetIdentifiedResults(ref IntPtr pInput, int count, bool deallocate)
|
||||
{
|
||||
Opc.Dx.IdentifiedResult[] output = null;
|
||||
|
||||
if (pInput != IntPtr.Zero && count > 0)
|
||||
{
|
||||
output = new Opc.Dx.IdentifiedResult[count];
|
||||
|
||||
IntPtr pos = pInput;
|
||||
|
||||
for (int ii = 0; ii < count; ii++)
|
||||
{
|
||||
OpcRcw.Dx.IdentifiedResult result = (OpcRcw.Dx.IdentifiedResult)Marshal.PtrToStructure(pos, typeof(OpcRcw.Dx.IdentifiedResult));
|
||||
|
||||
output[ii] = new Opc.Dx.IdentifiedResult();
|
||||
|
||||
output[ii].ItemName = result.szItemName;
|
||||
output[ii].ItemPath = result.szItemPath;
|
||||
output[ii].Version = result.szVersion;
|
||||
output[ii].ResultID = OpcCom.Interop.GetResultID(result.hResultCode);
|
||||
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.DestroyStructure(pos, typeof(OpcRcw.Dx.IdentifiedResult));
|
||||
}
|
||||
|
||||
pos = (IntPtr)(pos.ToInt64() + Marshal.SizeOf(typeof(OpcRcw.Dx.IdentifiedResult)));
|
||||
}
|
||||
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.FreeCoTaskMem(pInput);
|
||||
pInput = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an array of COM DXConnection structures to .NET DXConnection objects.
|
||||
/// </summary>
|
||||
internal static DXConnection[] GetDXConnections(ref IntPtr pInput, int count, bool deallocate)
|
||||
{
|
||||
DXConnection[] output = null;
|
||||
|
||||
if (pInput != IntPtr.Zero && count > 0)
|
||||
{
|
||||
output = new DXConnection[count];
|
||||
|
||||
IntPtr pos = pInput;
|
||||
|
||||
for (int ii = 0; ii < count; ii++)
|
||||
{
|
||||
OpcRcw.Dx.DXConnection connection = (OpcRcw.Dx.DXConnection)Marshal.PtrToStructure(pos, typeof(OpcRcw.Dx.DXConnection));
|
||||
|
||||
output[ii] = GetDXConnection(connection, deallocate);
|
||||
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.DestroyStructure(pos, typeof(OpcRcw.Dx.DXConnection));
|
||||
}
|
||||
|
||||
pos = (IntPtr)(pos.ToInt64() + Marshal.SizeOf(typeof(OpcRcw.Dx.DXConnection)));
|
||||
}
|
||||
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.FreeCoTaskMem(pInput);
|
||||
pInput = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an array of .NET DXConnection objects to COM DXConnection structures.
|
||||
/// </summary>
|
||||
internal static OpcRcw.Dx.DXConnection[] GetDXConnections(DXConnection[] input)
|
||||
{
|
||||
OpcRcw.Dx.DXConnection[] output = null;
|
||||
|
||||
if (input != null && input.Length > 0)
|
||||
{
|
||||
output = new OpcRcw.Dx.DXConnection[input.Length];
|
||||
|
||||
for (int ii = 0; ii < input.Length; ii++)
|
||||
{
|
||||
output[ii] = GetDXConnection(input[ii]);
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a .NET DXConnection object to COM DXConnection structure.
|
||||
/// </summary>
|
||||
internal static OpcRcw.Dx.DXConnection GetDXConnection(DXConnection input)
|
||||
{
|
||||
OpcRcw.Dx.DXConnection output = new OpcRcw.Dx.DXConnection();
|
||||
|
||||
// set output default values.
|
||||
output.dwMask = 0;
|
||||
output.szItemPath = null;
|
||||
output.szItemName = null;
|
||||
output.szVersion = null;
|
||||
output.dwBrowsePathCount = 0;
|
||||
output.pszBrowsePaths = IntPtr.Zero;
|
||||
output.szName = null;
|
||||
output.szDescription = null;
|
||||
output.szKeyword = null;
|
||||
output.bDefaultSourceItemConnected = 0;
|
||||
output.bDefaultTargetItemConnected = 0;
|
||||
output.bDefaultOverridden = 0;
|
||||
output.vDefaultOverrideValue = null;
|
||||
output.vSubstituteValue = null;
|
||||
output.bEnableSubstituteValue = 0;
|
||||
output.szTargetItemPath = null;
|
||||
output.szTargetItemName = null;
|
||||
output.szSourceServerName = null;
|
||||
output.szSourceItemPath = null;
|
||||
output.szSourceItemName = null;
|
||||
output.dwSourceItemQueueSize = 0;
|
||||
output.dwUpdateRate = 0;
|
||||
output.fltDeadBand = 0;
|
||||
output.szVendorData = null;
|
||||
|
||||
// item name
|
||||
if (input.ItemName != null)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.ItemName;
|
||||
output.szItemName = input.ItemName;
|
||||
}
|
||||
|
||||
// item path
|
||||
if (input.ItemPath != null)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.ItemPath;
|
||||
output.szItemPath = input.ItemPath;
|
||||
}
|
||||
|
||||
// version
|
||||
if (input.Version != null)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.Version;
|
||||
output.szVersion = input.Version;
|
||||
}
|
||||
|
||||
// browse paths
|
||||
if (input.BrowsePaths.Count > 0)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.BrowsePaths;
|
||||
output.dwBrowsePathCount = input.BrowsePaths.Count;
|
||||
output.pszBrowsePaths = OpcCom.Interop.GetUnicodeStrings(input.BrowsePaths.ToArray());
|
||||
}
|
||||
|
||||
// name
|
||||
if (input.Name != null)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.Name;
|
||||
output.szName = input.Name;
|
||||
}
|
||||
|
||||
// description
|
||||
if (input.Description != null)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.Description;
|
||||
output.szDescription = input.Description;
|
||||
}
|
||||
|
||||
// keyword
|
||||
if (input.Keyword != null)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.Keyword;
|
||||
output.szKeyword = input.Keyword;
|
||||
}
|
||||
|
||||
// default source item connected
|
||||
if (input.DefaultSourceItemConnectedSpecified)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.DefaultSourceItemConnected;
|
||||
output.bDefaultSourceItemConnected = (input.DefaultSourceItemConnected)?1:0;
|
||||
}
|
||||
|
||||
// default target item connected
|
||||
if (input.DefaultTargetItemConnectedSpecified)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.DefaultTargetItemConnected;
|
||||
output.bDefaultTargetItemConnected = (input.DefaultTargetItemConnected)?1:0;
|
||||
}
|
||||
|
||||
// default overridden
|
||||
if (input.DefaultOverriddenSpecified)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.DefaultOverridden;
|
||||
output.bDefaultOverridden = (input.DefaultOverridden)?1:0;
|
||||
}
|
||||
|
||||
// default override value
|
||||
if (input.DefaultOverrideValue != null)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.DefaultOverrideValue;
|
||||
output.vDefaultOverrideValue = input.DefaultOverrideValue;
|
||||
}
|
||||
|
||||
// substitute value
|
||||
if (input.SubstituteValue != null)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.SubstituteValue;
|
||||
output.vSubstituteValue = input.SubstituteValue;
|
||||
}
|
||||
|
||||
// enable substitute value
|
||||
if (input.EnableSubstituteValueSpecified)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.EnableSubstituteValue;
|
||||
output.bEnableSubstituteValue = (input.EnableSubstituteValue)?1:0;
|
||||
}
|
||||
|
||||
// target item name
|
||||
if (input.TargetItemName != null)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.TargetItemName;
|
||||
output.szTargetItemName = input.TargetItemName;
|
||||
}
|
||||
|
||||
// target item path
|
||||
if (input.TargetItemPath != null)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.TargetItemPath;
|
||||
output.szTargetItemPath = input.TargetItemPath;
|
||||
}
|
||||
|
||||
// source server name
|
||||
if (input.SourceServerName != null)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.SourceServerName;
|
||||
output.szSourceServerName = input.SourceServerName;
|
||||
}
|
||||
|
||||
// source item name
|
||||
if (input.SourceItemName != null)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.SourceItemName;
|
||||
output.szSourceItemName = input.SourceItemName;
|
||||
}
|
||||
|
||||
// source item path
|
||||
if (input.SourceItemPath != null)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.SourceItemPath;
|
||||
output.szSourceItemPath = input.SourceItemPath;
|
||||
}
|
||||
|
||||
// source item queue size
|
||||
if (input.SourceItemQueueSizeSpecified)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.SourceItemQueueSize;
|
||||
output.dwSourceItemQueueSize = input.SourceItemQueueSize;
|
||||
}
|
||||
|
||||
// update rate
|
||||
if (input.UpdateRateSpecified)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.UpdateRate;
|
||||
output.dwUpdateRate = input.UpdateRate;
|
||||
}
|
||||
|
||||
// deadband
|
||||
if (input.DeadbandSpecified)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.DeadBand;
|
||||
output.fltDeadBand = input.Deadband;
|
||||
}
|
||||
|
||||
// vendor data
|
||||
if (input.VendorData != null)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.VendorData;
|
||||
output.szVendorData = input.VendorData;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a COM DXConnection structure to a .NET DXConnection object.
|
||||
/// </summary>
|
||||
internal static DXConnection GetDXConnection(OpcRcw.Dx.DXConnection input, bool deallocate)
|
||||
{
|
||||
DXConnection output = new DXConnection();
|
||||
|
||||
// set output default values.
|
||||
output.ItemPath = null;
|
||||
output.ItemName = null;
|
||||
output.Version = null;
|
||||
output.BrowsePaths.Clear();
|
||||
output.Name = null;
|
||||
output.Description = null;
|
||||
output.Keyword = null;
|
||||
output.DefaultSourceItemConnected = false;
|
||||
output.DefaultSourceItemConnectedSpecified = false;
|
||||
output.DefaultTargetItemConnected = false;
|
||||
output.DefaultTargetItemConnectedSpecified = false;
|
||||
output.DefaultOverridden = false;
|
||||
output.DefaultOverriddenSpecified = false;
|
||||
output.DefaultOverrideValue = null;
|
||||
output.SubstituteValue = null;
|
||||
output.EnableSubstituteValue = false;
|
||||
output.EnableSubstituteValueSpecified = false;
|
||||
output.TargetItemPath = null;
|
||||
output.TargetItemName = null;
|
||||
output.SourceServerName = null;
|
||||
output.SourceItemPath = null;
|
||||
output.SourceItemName = null;
|
||||
output.SourceItemQueueSize = 0;
|
||||
output.SourceItemQueueSizeSpecified = false;
|
||||
output.UpdateRate = 0;
|
||||
output.UpdateRateSpecified = false;
|
||||
output.Deadband = 0;
|
||||
output.DeadbandSpecified = false;
|
||||
output.VendorData = null;
|
||||
|
||||
// item name
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.ItemName) != 0)
|
||||
{
|
||||
output.ItemName = input.szItemName;
|
||||
}
|
||||
|
||||
// item path
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.ItemPath) != 0)
|
||||
{
|
||||
output.ItemPath = input.szItemPath;
|
||||
}
|
||||
|
||||
// version
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.Version) != 0)
|
||||
{
|
||||
output.Version = input.szVersion;
|
||||
}
|
||||
|
||||
// browse paths
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.BrowsePaths) != 0)
|
||||
{
|
||||
string[] browsePaths = OpcCom.Interop.GetUnicodeStrings(ref input.pszBrowsePaths, input.dwBrowsePathCount, deallocate);
|
||||
|
||||
if (browsePaths != null)
|
||||
{
|
||||
output.BrowsePaths.AddRange(browsePaths);
|
||||
}
|
||||
}
|
||||
|
||||
// name
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.Name) != 0)
|
||||
{
|
||||
output.Name = input.szName;
|
||||
}
|
||||
|
||||
// description
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.Description) != 0)
|
||||
{
|
||||
output.Description = input.szDescription;
|
||||
}
|
||||
|
||||
// keyword
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.Keyword) != 0)
|
||||
{
|
||||
output.Keyword = input.szKeyword;
|
||||
}
|
||||
|
||||
// default source item connected
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.DefaultSourceItemConnected) != 0)
|
||||
{
|
||||
output.DefaultSourceItemConnected = input.bDefaultSourceItemConnected != 0;
|
||||
output.DefaultSourceItemConnectedSpecified = true;
|
||||
}
|
||||
|
||||
// default target item connected
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.DefaultTargetItemConnected) != 0)
|
||||
{
|
||||
output.DefaultTargetItemConnected = input.bDefaultTargetItemConnected != 0;
|
||||
output.DefaultTargetItemConnectedSpecified = true;
|
||||
}
|
||||
|
||||
// default overridden
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.DefaultOverridden) != 0)
|
||||
{
|
||||
output.DefaultOverridden = input.bDefaultOverridden != 0;
|
||||
output.DefaultOverriddenSpecified = true;
|
||||
}
|
||||
|
||||
// default override value
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.DefaultOverrideValue) != 0)
|
||||
{
|
||||
output.DefaultOverrideValue = input.vDefaultOverrideValue;
|
||||
}
|
||||
|
||||
// substitute value
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.SubstituteValue) != 0)
|
||||
{
|
||||
output.SubstituteValue = input.vSubstituteValue;
|
||||
}
|
||||
|
||||
// enable substitute value
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.EnableSubstituteValue) != 0)
|
||||
{
|
||||
output.EnableSubstituteValue = input.bEnableSubstituteValue != 0;
|
||||
output.EnableSubstituteValueSpecified = true;
|
||||
}
|
||||
|
||||
// target item name
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.TargetItemName) != 0)
|
||||
{
|
||||
output.TargetItemName = input.szTargetItemName;
|
||||
}
|
||||
|
||||
// target item path
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.TargetItemPath) != 0)
|
||||
{
|
||||
output.TargetItemPath = input.szTargetItemPath;
|
||||
}
|
||||
|
||||
// source server name
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.SourceServerName) != 0)
|
||||
{
|
||||
output.SourceServerName = input.szSourceServerName;
|
||||
}
|
||||
|
||||
// source item name
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.SourceItemName) != 0)
|
||||
{
|
||||
output.SourceItemName = input.szSourceItemName;
|
||||
}
|
||||
|
||||
// source item path
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.SourceItemPath) != 0)
|
||||
{
|
||||
output.SourceItemPath = input.szSourceItemPath;
|
||||
}
|
||||
|
||||
// source item queue size
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.SourceItemQueueSize) != 0)
|
||||
{
|
||||
output.SourceItemQueueSize = input.dwSourceItemQueueSize;
|
||||
output.SourceItemQueueSizeSpecified = true;
|
||||
}
|
||||
|
||||
// update rate
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.UpdateRate) != 0)
|
||||
{
|
||||
output.UpdateRate = input.dwUpdateRate;
|
||||
output.UpdateRateSpecified = true;
|
||||
}
|
||||
|
||||
// deadband
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.DeadBand) != 0)
|
||||
{
|
||||
output.Deadband = input.fltDeadBand;
|
||||
output.DeadbandSpecified = true;
|
||||
}
|
||||
|
||||
// vendor data
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.VendorData) != 0)
|
||||
{
|
||||
output.VendorData = input.szVendorData;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an array of .NET ItemIdentifier objects to COM ItemIdentifier structures.
|
||||
/// </summary>
|
||||
internal static OpcRcw.Dx.ItemIdentifier[] GetItemIdentifiers(Opc.Dx.ItemIdentifier[] input)
|
||||
{
|
||||
OpcRcw.Dx.ItemIdentifier[] output = null;
|
||||
|
||||
if (input != null && input.Length > 0)
|
||||
{
|
||||
output = new OpcRcw.Dx.ItemIdentifier[input.Length];
|
||||
|
||||
for (int ii = 0; ii < input.Length; ii++)
|
||||
{
|
||||
output[ii] = new OpcRcw.Dx.ItemIdentifier();
|
||||
|
||||
output[ii].szItemName = input[ii].ItemName;
|
||||
output[ii].szItemPath = input[ii].ItemPath;
|
||||
output[ii].szVersion = input[ii].Version;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user