Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
T
Thick-Common
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
潘栩锋
Thick-Common
Commits
9ca3b355
Commit
9ca3b355
authored
Nov 23, 2022
by
潘栩锋
🚴
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
添加 异步的modbusRTU
parent
3e0dd78a
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
513 additions
and
0 deletions
+513
-0
GeneralGommunication.csproj
GeneralGommunication/GeneralGommunication.csproj
+1
-0
ModbusRTUAsync.cs
GeneralGommunication/ModbusRTUAsync.cs
+512
-0
No files found.
GeneralGommunication/GeneralGommunication.csproj
View file @
9ca3b355
...
@@ -49,6 +49,7 @@
...
@@ -49,6 +49,7 @@
<Compile
Include=
"IGeneralComm.cs"
/>
<Compile
Include=
"IGeneralComm.cs"
/>
<Compile
Include=
"IModbusBase.cs"
/>
<Compile
Include=
"IModbusBase.cs"
/>
<Compile
Include=
"ModbusASCII.cs"
/>
<Compile
Include=
"ModbusASCII.cs"
/>
<Compile
Include=
"ModbusRTUAsync.cs"
/>
<Compile
Include=
"ModbusRTU.cs"
/>
<Compile
Include=
"ModbusRTU.cs"
/>
<Compile
Include=
"Properties\AssemblyInfo.cs"
/>
<Compile
Include=
"Properties\AssemblyInfo.cs"
/>
<Compile
Include=
"Protocol7ECommon.cs"
/>
<Compile
Include=
"Protocol7ECommon.cs"
/>
...
...
GeneralGommunication/ModbusRTUAsync.cs
0 → 100644
View file @
9ca3b355
using
Newtonsoft.Json.Linq
;
using
NLog
;
using
System
;
using
System.Collections.Generic
;
using
System.ComponentModel
;
using
System.Diagnostics
;
using
System.Linq
;
using
System.Security.Cryptography
;
using
System.Text
;
using
System.Threading.Tasks
;
using
System.Windows.Threading
;
using
static
GeneralGommunication
.
Modbus_Transaction
;
namespace
GeneralGommunication
{
/// <summary>
/// 异步modbus RTU
/// </summary>
public
class
ModbusRTUAsync
{
public
Logger
logger
=
NLog
.
LogManager
.
GetCurrentClassLogger
();
public
event
PropertyChangedEventHandler
PropertyChanged
;
/// <summary>
/// 包出错次数
/// </summary>
public
int
ErrCnt
{
get
;
protected
set
;
}
/// <summary>
/// 连接成功;
/// 当命令多次发送失败,IsConnected = false
/// </summary>
public
bool
IsConnected
{
get
;
private
set
;
}
/// <summary>
/// 通讯速度测量模块
/// </summary>
public
CommSpeedMeasuring
Csm
=>
csm
;
/// <summary>
/// 有数据需要发送
/// </summary>
public
event
SendMsgEventHander
SendMsgEvent
;
/// <summary>
/// 对于全部 有返回的函数调用,都使用Dispatcher,使线程同步
/// </summary>
public
Dispatcher
Dispatcher
;
List
<
byte
>
currPack
=
new
List
<
byte
>();
/// <summary>
/// 指令队列,必须等上1条指令回复了,才能发下条指令
/// </summary>
List
<
Modbus_Transaction
>
Transactions
;
/// <summary>
/// 当前正在等待回复的指令
/// </summary>
Modbus_Transaction
currTran
;
/// <summary>
/// currTran 发送后,开始计时
/// </summary>
Stopwatch
stopwatch_timeOut
;
CommSpeedMeasuring
csm
=
new
CommSpeedMeasuring
();
/// <summary>
/// currTran 重发次数
/// </summary>
int
retryCnt
=
0
;
public
ModbusRTUAsync
()
{
Transactions
=
new
List
<
Modbus_Transaction
>();
stopwatch_timeOut
=
new
Stopwatch
();
}
public
void
Init
()
{
}
/// <summary>
/// 发送指令的超时判断
/// </summary>
public
void
OnPoll_TimeOut
()
{
//TODO 要处理 TimeOut / ParseFuncPack / GetSendMsg 线性同步问题
if
(!
IsConnected
)
return
;
if
(
currTran
==
null
)
return
;
//没有指令
if
(!
stopwatch_timeOut
.
IsRunning
)
return
;
//还没开始发送
if
(
stopwatch_timeOut
.
Elapsed
<
TimeSpan
.
FromSeconds
(
1
))
return
;
//发送到现在,还没到1秒,继续等
//大于1秒也没回复,异常
//重试3次
retryCnt
++;
stopwatch_timeOut
.
Stop
();
//停止,等下次发送
if
(
retryCnt
>=
3
)
{
//已经重试了3次,放弃
IsConnected
=
false
;
currTran
=
null
;
//清空 指令队列
Transactions
.
Clear
();
return
;
}
else
{
//再发一次指令
SendMsgEvent
?.
Invoke
(
this
);
}
}
public
void
RecMsg
(
byte
[]
recBuf
)
{
IsConnected
=
true
;
csm
.
IncRec
(
recBuf
.
Count
());
currPack
.
AddRange
(
recBuf
);
if
(
logger
.
IsDebugEnabled
)
{
string
msg
=
bytes2hex
(
recBuf
);
logger
.
Debug
(
$"REC
{
msg
}
"
);
}
ParsePack
();
//OnPoll_TimeOut();
}
/// <summary>
/// 获取 发送队列 第1条msg
/// </summary>
/// <returns></returns>
public
byte
[]
GetSendMsg
()
{
//TODO 要处理 TimeOut / ParseFuncPack / GetSendMsg 线性同步问题
if
(
currTran
==
null
)
{
//当前没有指令正在发送
if
(
Transactions
.
Count
()
==
0
)
//队列没有需要发送的指令
return
null
;
currTran
=
Transactions
.
First
();
retryCnt
=
0
;
Transactions
.
RemoveAt
(
0
);
}
else
{
//发送出去中,等待回复
if
(
stopwatch_timeOut
.
IsRunning
)
return
null
;
//已经发送了,计时器都启动了
}
var
tran
=
currTran
;
//调试: 打印发送信息
if
(
logger
.
IsDebugEnabled
)
{
string
msg
=
$"REQ N:
{
tran
.
deviceNo
}
F:
{
tran
.
func
:
00
}
A:
{
tran
.
addr
}
C:
{
tran
.
cnt
}
"
;
if
(
tran
.
desription
!=
null
)
msg
+=
tran
.
desription
;
logger
.
Debug
(
msg
);
//msg = bytes2hex(tran.sendBuf);
//logger.Debug($"SEND {msg}");
}
//开始计时
stopwatch_timeOut
.
Restart
();
return
tran
.
sendBuf
.
ToArray
();
}
/// <summary>
/// 复位全部状态,通常由于通讯模块检测到连接断开导致的
/// </summary>
public
void
ResetMsg
()
{
currTran
=
null
;
stopwatch_timeOut
.
Stop
();
csm
.
Reset
();
IsConnected
=
false
;
Transactions
.
Clear
();
}
string
bytes2hex
(
IEnumerable
<
byte
>
pack
)
{
StringBuilder
sb
=
new
StringBuilder
();
for
(
int
i
=
0
;
i
<
pack
.
Count
();
i
++)
{
sb
.
Append
(
$"
{
pack
.
ElementAt
(
i
):
X2
}
"
);
}
return
sb
.
ToString
();
}
/// <summary>
/// 包解析
/// </summary>
/// <param name="datas"></param>
protected
void
ParsePack
()
{
if
(
currTran
==
null
)
{
//没有请求。。。
currPack
.
Clear
();
return
;
}
if
(
currPack
.
Count
()
<
3
)
{
//数据太少
return
;
}
if
(
currPack
[
0
]
!=
currTran
.
deviceNo
)
{
logger
.
Error
(
$"ACK 接收的SLAVE地址
{
currPack
[
0
]}
与发送的
{
currTran
.
deviceNo
}
不一致"
);
ErrCnt
++;
currPack
.
Clear
();
return
;
}
if
(
currPack
[
1
]
!=
currTran
.
func
)
{
logger
.
Error
(
$"ACK 错误码:
{
currPack
[
1
]}
异常码:
{
currPack
[
2
]}
"
);
ErrCnt
++;
currPack
.
Clear
();
return
;
}
if
(
currPack
.
Count
()
<
currTran
.
expRecBytes
)
return
;
//数据还没收完
UInt16
crc
=
currPack
.
CRC16
(
0
,
currTran
.
expRecBytes
-
2
);
UInt16
rec_crc
=
currPack
.
ToUInt16_Little_Endian
(
currTran
.
expRecBytes
-
2
);
if
(
crc
!=
rec_crc
)
{
logger
.
Error
(
$"ACK 指令码:
{
currTran
.
func
:
X2
}
CRC 校验出错 接收:
{
rec_crc
:
X4
}
计算:
{
crc
:
X4
}
"
);
ErrCnt
++;
currPack
.
Clear
();
return
;
}
//成功解析出一个包
currTran
.
funcData
.
AddRange
(
currPack
.
Skip
(
2
).
Take
(
currTran
.
expRecBytes
-
4
));
csm
.
IncPack
(
1
);
currPack
.
Clear
();
//停止超时检测
stopwatch_timeOut
.
Stop
();
switch
(
currTran
.
func
)
{
case
0x03
:
{
List
<
UInt16
>
values
=
new
List
<
UInt16
>();
int
index
=
1
;
while
(
index
<
currTran
.
funcData
.
Count
())
{
values
.
Add
(
currTran
.
funcData
.
ToUInt16_Big_Endian
(
index
));
index
+=
2
;
}
currTran
.
retData
=
currTran
.
parse16FuncPack
(
values
);
}
break
;
}
if
(
logger
.
IsDebugEnabled
)
{
if
(
currTran
.
retData
==
null
)
logger
.
Debug
(
$"ACK"
);
else
logger
.
Debug
(
$"ACK
{
Newtonsoft
.
Json
.
JsonConvert
.
SerializeObject
(
currTran
.
retData
)}
"
);
}
//有很多指令是没有回复数据的, 回调只是通知 指令已经执行了而已
//调用回调
if
(
currTran
.
asyncDelegate
!=
null
)
{
if
(
Dispatcher
!=
null
)
//线程同步执行
{
Dispatcher
.
BeginInvoke
(
currTran
.
asyncDelegate
,
currTran
.
asyncContext
,
currTran
.
retData
);
}
else
{
currTran
.
asyncDelegate
.
Invoke
(
currTran
.
asyncContext
,
currTran
.
retData
);
}
}
//空出当前交易位置
currTran
=
null
;
if
(
Transactions
.
Count
()
>
0
)
{
//队列还有需要发送的指令,通知外部获取指令发送
SendMsgEvent
?.
Invoke
(
this
);
}
}
public
void
AddTran
(
Modbus_Transaction
tran
)
{
//放入 交易队列
Transactions
.
Add
(
tran
);
if
(
currTran
==
null
)
{
//当前没有指令正在发送
SendMsgEvent
?.
Invoke
(
this
);
}
}
/// <summary>
/// 读多个Holding REGs
/// </summary>
/// <param name="deviceNo"></param>
/// <param name="addr"></param>
/// <param name="cnt"></param>
/// <param name="desription"></param>
/// <param name="parseU16FuncPack"></param>
/// <param name="asyncDelegate"></param>
/// <param name="asyncContext"></param>
/// <returns></returns>
public
Modbus_Transaction
Do_03
(
byte
deviceNo
,
int
addr
,
int
cnt
,
string
desription
,
ParseU16FuncPackHandler
parseU16FuncPack
,
CallBackHandler
asyncDelegate
,
object
asyncContext
)
{
Modbus_Transaction
tran
=
new
Modbus_Transaction
()
{
deviceNo
=
deviceNo
,
func
=
0x03
,
addr
=
addr
,
cnt
=
cnt
,
expRecBytes
=
cnt
*
2
+
5
,
desription
=
desription
,
parse16FuncPack
=
parseU16FuncPack
,
asyncDelegate
=
asyncDelegate
,
asyncContext
=
asyncContext
};
tran
.
funcData
.
AddRange
(((
UInt16
)
addr
).
GetBytes_Big_endian
());
tran
.
funcData
.
AddRange
(((
UInt16
)
cnt
).
GetBytes_Big_endian
());
ToSendBuf
(
tran
);
return
tran
;
}
void
ToSendBuf
(
Modbus_Transaction
tran
)
{
var
data
=
tran
.
sendBuf
;
data
.
Add
(
tran
.
deviceNo
);
data
.
Add
(
tran
.
func
);
data
.
AddRange
(
tran
.
funcData
);
UInt16
crc
=
data
.
CRC16
(
0
,
data
.
Count
());
data
.
AddRange
(
crc
.
GetBytes_Little_Endian
());
tran
.
funcData
.
Clear
();
}
/// <summary>
/// 写单个 Holding REG
/// </summary>
/// <param name="deviceNo"></param>
/// <param name="addr"></param>
/// <param name="cnt"></param>
/// <param name="desription"></param>
/// <param name="asyncDelegate"></param>
/// <param name="asyncContext"></param>
/// <returns></returns>
public
Modbus_Transaction
Do_06
(
byte
deviceNo
,
int
addr
,
UInt16
value
,
string
desription
,
CallBackHandler
asyncDelegate
,
object
asyncContext
)
{
Modbus_Transaction
tran
=
new
Modbus_Transaction
()
{
deviceNo
=
deviceNo
,
func
=
0x06
,
addr
=
addr
,
expRecBytes
=
8
,
desription
=
desription
,
asyncDelegate
=
asyncDelegate
,
asyncContext
=
asyncContext
};
tran
.
funcData
.
AddRange
(((
UInt16
)
addr
).
GetBytes_Big_endian
());
tran
.
funcData
.
AddRange
(((
UInt16
)
value
).
GetBytes_Big_endian
());
ToSendBuf
(
tran
);
return
tran
;
}
/// <summary>
/// 写多个 Holding REG
/// </summary>
/// <param name="deviceNo"></param>
/// <param name="addr"></param>
/// <param name="cnt"></param>
/// <param name="desription"></param>
/// <param name="asyncDelegate"></param>
/// <param name="asyncContext"></param>
/// <returns></returns>
public
Modbus_Transaction
Do_10
(
byte
deviceNo
,
int
addr
,
IEnumerable
<
UInt16
>
datas
,
string
desription
,
CallBackHandler
asyncDelegate
,
object
asyncContext
)
{
Modbus_Transaction
tran
=
new
Modbus_Transaction
()
{
deviceNo
=
deviceNo
,
func
=
0x10
,
addr
=
addr
,
expRecBytes
=
8
,
desription
=
desription
,
asyncDelegate
=
asyncDelegate
,
asyncContext
=
asyncContext
};
tran
.
funcData
.
AddRange
(((
UInt16
)
tran
.
addr
).
GetBytes_Big_endian
());
tran
.
funcData
.
AddRange
(((
UInt16
)
datas
.
Count
()).
GetBytes_Big_endian
());
tran
.
funcData
.
Add
((
byte
)(
datas
.
Count
()
*
2
));
for
(
int
i
=
0
;
i
<
datas
.
Count
();
i
++)
{
tran
.
funcData
.
AddRange
(
datas
.
ElementAt
(
i
).
GetBytes_Big_endian
());
}
ToSendBuf
(
tran
);
return
tran
;
}
}
public
class
Modbus_Transaction
{
/// <summary>
/// 交易描述
/// </summary>
public
string
desription
;
/// <summary>
/// 设备编号 0~255 0是广播
/// </summary>
public
byte
deviceNo
;
/// <summary>
/// modbus 功能号
/// </summary>
public
byte
func
;
/// <summary>
/// 数据地址
/// </summary>
public
int
addr
;
/// <summary>
/// 操作的寄存器数量
/// </summary>
public
int
cnt
;
/// <summary>
/// 期待接收的数据量
/// </summary>
public
int
expRecBytes
;
public
List
<
byte
>
sendBuf
=
new
List
<
byte
>();
/// <summary>
/// 功能 的数据
/// </summary>
public
List
<
byte
>
funcData
=
new
List
<
byte
>();
public
delegate
object
ParseU16FuncPackHandler
(
List
<
UInt16
>
values
);
/// <summary>
/// 数据回复处理
/// </summary>
public
ParseU16FuncPackHandler
parse16FuncPack
;
public
delegate
object
ParseBoolFuncPackHandler
(
List
<
bool
>
values
);
/// <summary>
/// 数据回复处理
/// </summary>
public
ParseBoolFuncPackHandler
parseBoolFuncPack
;
/// <summary>
/// 回复的数据,只用于调试而已
/// </summary>
public
object
retData
;
/// <summary>
/// 回复 callback
/// </summary>
public
CallBackHandler
asyncDelegate
;
/// <summary>
/// 上下文
/// </summary>
public
object
asyncContext
;
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment