Browse Source

First commit

master
Z 5 months ago
commit
5c3e1143b9
  1. 2
      .gitattributes
  2. 21
      .gitignore
  3. 17
      EGFramework.csproj
  4. 19
      EGFramework.sln
  5. 79
      Example/Gateway/Gateway.tscn
  6. 883
      Example/Gateway/GatewayManual/BacnetGatewayManual_CN.md
  7. 12
      Example/Gateway/ModbusGateway.tscn
  8. 13
      Example/Gateway/Script/Data/DataBacNetGatewaySetting.cs
  9. 31
      Example/Gateway/Script/Data/DataModbusGatewaySettings.cs
  10. 16
      Example/Gateway/Script/Data/DataTcpGatewaySetting.cs
  11. 6
      Example/Gateway/Script/Interface/IGateway.cs
  12. 73
      Example/Gateway/Script/View/ViewBacNetGateway.cs
  13. 57
      Example/Gateway/Script/View/ViewBacnetHttpServer.cs
  14. 23
      Example/Gateway/Script/View/ViewGateway.cs
  15. 69
      Example/Gateway/Script/View/ViewModbusGateway.cs
  16. 109
      Example/Gateway/Script/View/ViewTcpGateway.cs
  17. 154
      Example/ModbusDebugTool/Component/modbus_item.tscn
  18. 32
      Example/ModbusDebugTool/Script/DataModbusItem.cs
  19. 102
      Example/ModbusDebugTool/Script/ViewEdit.cs
  20. 78
      Example/ModbusDebugTool/Script/ViewMenu.cs
  21. 33
      Example/ModbusDebugTool/Script/ViewMessage.cs
  22. 99
      Example/ModbusDebugTool/Script/ViewModbusItem.cs
  23. 55
      Example/ModbusDebugTool/Script/ViewSettings.cs
  24. 417
      Example/ModbusDebugTool/TestModbus.tscn
  25. 3
      Example/Theme/White/White.tres
  26. 27
      Example/UsingTest/Scene/TestEGFramework.tscn
  27. 45
      Example/UsingTest/Script/EGMDnsTest.cs
  28. 313
      Example/UsingTest/Script/EGTest.cs
  29. BIN
      Font/SourceHanSansCN-Regular.otf
  30. 33
      Font/SourceHanSansCN-Regular.otf.import
  31. 9
      LICENSE
  32. 210
      ReadMe.md
  33. 211
      addons/EGFramework/EGFramework.cs
  34. 21
      addons/EGFramework/License_Third_Part/BACnet/MIT_license.txt
  35. 22
      addons/EGFramework/License_Third_Part/MQTTnet/LICENSE
  36. 23
      addons/EGFramework/License_Third_Part/Microsoft_Data_SQLite/LICENSE.txt
  37. 20
      addons/EGFramework/License_Third_Part/NewtonSoft_Json/LICENSE.md
  38. 21
      addons/EGFramework/License_Third_Part/WebDavClient/LICENSE.txt
  39. 96
      addons/EGFramework/License_Third_Part/source_han_sans/LICENSE.txt
  40. 56
      addons/EGFramework/Module/EGCQRS.cs
  41. 107
      addons/EGFramework/Module/EGEvent.cs
  42. 324
      addons/EGFramework/Module/EGMessage.cs
  43. 65
      addons/EGFramework/Module/EGObjects.cs
  44. 218
      addons/EGFramework/Module/Extension/EGConvertExtension.cs
  45. 154
      addons/EGFramework/Module/Extension/EGCrcExtension.cs
  46. 20
      addons/EGFramework/Module/Extension/EGDateTimeExtension.cs
  47. 78
      addons/EGFramework/Module/Extension/EGEncodingExtension.cs
  48. 54
      addons/EGFramework/Module/Extension/EGIpExtension.cs
  49. 48
      addons/EGFramework/Module/NodeExtension/EGCreate.cs
  50. 29
      addons/EGFramework/Module/NodeExtension/EGNode.cs
  51. 9
      addons/EGFramework/Module/NodeExtension/Tween/EGTween.cs
  52. 299
      addons/EGFramework/Module/ProtocolExtension/EGDnsExtension.cs
  53. 871
      addons/EGFramework/Module/ProtocolExtension/EGModbusExtension.cs
  54. 573
      addons/EGFramework/Module/ProtocolTools/EGBacnet.cs
  55. 100
      addons/EGFramework/Module/ProtocolTools/EGFileStream.cs
  56. 96
      addons/EGFramework/Module/ProtocolTools/EGHttpClient.cs
  57. 148
      addons/EGFramework/Module/ProtocolTools/EGHttpServer.cs
  58. 163
      addons/EGFramework/Module/ProtocolTools/EGMQTT.cs
  59. 140
      addons/EGFramework/Module/ProtocolTools/EGModbus.cs
  60. 71
      addons/EGFramework/Module/ProtocolTools/EGProtocolSchedule.cs
  61. 216
      addons/EGFramework/Module/ProtocolTools/EGSerialPort.cs
  62. 181
      addons/EGFramework/Module/ProtocolTools/EGTCPClient.cs
  63. 138
      addons/EGFramework/Module/ProtocolTools/EGTCPServer.cs
  64. 123
      addons/EGFramework/Module/ProtocolTools/EGUDP.cs
  65. 19
      addons/EGFramework/Module/ProtocolTools/ProtocolToolsInterface.cs
  66. 247
      addons/EGFramework/Module/SQL/EGSqlite.cs
  67. 122
      addons/EGFramework/Module/SaveTools/EGSave.cs
  68. 230
      addons/EGFramework/Module/WebDav/EGWebDav.cs
  69. 1
      icon.svg
  70. 37
      icon.svg.import
  71. 26
      project.godot

2
.gitattributes vendored

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
# Normalize EOL for all files that Git considers text files.
* text=auto eol=lf

21
.gitignore vendored

@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
# Godot 4+ specific ignores
.godot/
# Godot-specific ignores
.import/
export.cfg
export_presets.cfg
# Imported translations (automatically generated from CSV files)
*.translation
# Mono-specific ignores
.mono/
data_*/
mono_crash.*.json
# VScode-Specific ignores
.vscode/
#EGSave-Specific ignores
SaveData/

17
EGFramework.csproj

@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
<Project Sdk="Godot.NET.Sdk/4.2.1">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework Condition=" '$(GodotTargetPlatform)' == 'android' ">net7.0</TargetFramework>
<TargetFramework Condition=" '$(GodotTargetPlatform)' == 'ios' ">net8.0</TargetFramework>
<EnableDynamicLoading>true</EnableDynamicLoading>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.IO.Ports" Version="8.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.1" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" />
<PackageReference Include="WebDav.Client" Version="2.8.0" />
<PackageReference Include="MQTTnet" Version="4.3.3.952" />
<PackageReference Include="BACnet" Version="2.0.4" />
</ItemGroup>
</Project>

19
EGFramework.sln

@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2012
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EGFramework", "EGFramework.csproj", "{C87FD7C2-9AA4-44E6-9B4C-8EEADF9D3E09}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
ExportDebug|Any CPU = ExportDebug|Any CPU
ExportRelease|Any CPU = ExportRelease|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C87FD7C2-9AA4-44E6-9B4C-8EEADF9D3E09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C87FD7C2-9AA4-44E6-9B4C-8EEADF9D3E09}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C87FD7C2-9AA4-44E6-9B4C-8EEADF9D3E09}.ExportDebug|Any CPU.ActiveCfg = ExportDebug|Any CPU
{C87FD7C2-9AA4-44E6-9B4C-8EEADF9D3E09}.ExportDebug|Any CPU.Build.0 = ExportDebug|Any CPU
{C87FD7C2-9AA4-44E6-9B4C-8EEADF9D3E09}.ExportRelease|Any CPU.ActiveCfg = ExportRelease|Any CPU
{C87FD7C2-9AA4-44E6-9B4C-8EEADF9D3E09}.ExportRelease|Any CPU.Build.0 = ExportRelease|Any CPU
EndGlobalSection
EndGlobal

79
Example/Gateway/Gateway.tscn

@ -0,0 +1,79 @@ @@ -0,0 +1,79 @@
[gd_scene load_steps=6 format=3 uid="uid://hp4ae14r3hpm"]
[ext_resource type="Script" path="res://Example/Gateway/Script/View/ViewGateway.cs" id="1_tas7d"]
[ext_resource type="Script" path="res://Example/Gateway/Script/View/ViewTcpGateway.cs" id="2_ct4oh"]
[ext_resource type="Script" path="res://Example/Gateway/Script/View/ViewBacNetGateway.cs" id="3_ip8nj"]
[ext_resource type="Script" path="res://Example/Gateway/Script/View/ViewBacnetHttpServer.cs" id="4_plubp"]
[ext_resource type="FontFile" uid="uid://b1atsgy4xkk7d" path="res://Font/SourceHanSansCN-Regular.otf" id="5_q4lsg"]
[node name="Gateway" type="Control"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_tas7d")
[node name="TcpGateway" type="Control" parent="."]
visible = false
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("2_ct4oh")
[node name="BacNetGateway" type="Control" parent="."]
visible = false
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("3_ip8nj")
[node name="BacnetHttpServer" type="Control" parent="."]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("4_plubp")
[node name="Setting" type="Control" parent="."]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="GatewaySetting" type="Label" parent="Setting"]
layout_mode = 1
anchors_preset = 5
anchor_left = 0.5
anchor_right = 0.5
offset_left = -181.5
offset_right = 181.5
offset_bottom = 70.0
grow_horizontal = 2
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
theme_override_constants/shadow_offset_x = 4
theme_override_constants/shadow_offset_y = 4
theme_override_fonts/font = ExtResource("5_q4lsg")
theme_override_font_sizes/font_size = 48
text = "Gateway Setting"
[node name="MessageContainer" type="TextEdit" parent="Setting"]
layout_mode = 1
anchors_preset = -1
anchor_top = 0.56
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
editable = false

883
Example/Gateway/GatewayManual/BacnetGatewayManual_CN.md

@ -0,0 +1,883 @@ @@ -0,0 +1,883 @@
# Bacnet软网关文档
---
# 拓扑一览
Bacnet设备<------>Bacnet软网关<-------->第三方应用
# 1.网关配置
使用HttpServer模式时,需要注意如果非127.0.0.1这个回环地址,需要手动添加prefix权限或者以管理员身份运行该网关。
目前是以Json来进行网关的启动配置项,后续会新增相关GUI优化使用体验。
## 配置路径
配置网关的配置文件存放于 `SaveData\Default.json`这个文件内,找到如下片段
```json
"EGFramework.Examples.MqttGateway.DataBacnetGatewaySetting": {
"MqttHost": "192.168.1.220",
"HttpServerPrefix": "http://127.0.0.1:5000/",
"RequestTheme": "/LocalBacnetRequest",
"ResponseTheme": "/LocalBacnetResponse"
}
```
## 配置说明(此处值均为String)
| 声明 | 键值 | 示例 | 备注 |
| ----------------- | ---------------- | ---------------------- | ---------------- |
| MQTT服务器地址(string) | MqttHost | 192.168.1.220 | 需要改成实际的服务器地址 |
| HttpServer前缀地址 | HttpServerPrefix | http://127.0.0.1:5000/ | 需要以`/`符号结尾 |
| 请求主题(string) | RequestTheme | /LocalBacnetRequest | 发布请求的订阅地址,参考1.1 |
| 响应主题(string) | ResponseTheme | /LocalBacnetResponse | 接受响应的的订阅地址,参考1.2 |
## 补充说明
### 1.1 请求主题
开发第三方应用时,您仅需要把请求的内容发布到该主题下即可,网关会自动订阅该主题并接受该主题发布的所有消息,并将处理结果发布在响应主题上。
### 1.2 响应主题
开发第三方应用时,您需要订阅该主题来接收处理结果的响应,根据您发布到请求的内容,网关请求物理设备的处理结果会发布到该主题下,您需要根据内容自行判断是哪条请求的响应。
### 1.3 关于HttpSever模式
如果您使用httpServer作为网关请求时,仅需请求对应的接口地址即可。
# 2.WhoIs 设备列表查询
## http接口地址
/WhoIs
## 请求结构说明
Bacnet请求构成
| 声明 | 键值 | 示例 | 备注 |
| -------------- | ------------ | -------------- | ------------- |
| 功能码(string) | FunctionCode | OperateRequest | 固定值 |
| 操作码(Enum-uint) | OperateCode | 0~1024 | 需要查询操作码对照表T-0 |
请求案例
注意:MQTT请求设备列表时,需要等待2秒的设备扫描时间,可能不会立刻返回设备列表的结果。HttpServer不会出现此情况,但Http请求时仅使用设备缓存。
示例:
```json
{
"FunctionCode":"OperateRequest",
"OperateCode":2
}
```
响应案例
```json
{
"DevicesList": [
1
],
"FunctionCode": "WhoIsResponse"
}
```
# 3.Bacnet 单寄存器读写
## http接口地址
【读单个寄存器】/ReadRegisterProperty
【写单个寄存器】/WriteRegisterProperty
## 请求结构说明
Bacnet请求构成
| 声明 | 键值 | 示例 | 备注 |
| ------------------- | --------------- | -------------- | ----------------------- |
| 功能码(string) | FunctionCode | OperateRequest | 固定值 |
| 操作码(Enum-uint) | OperateCode | 0~1024 | 需要查询操作码对照表T-0 |
| 设备ID(uint) | DeviceId | 0~4294967295 | |
| 寄存器类型(Enum-uint) | ObjectTypes | 0~1024 | 需要查询寄存器类型对照表T-1 |
| 寄存器地址(uint) | RegisterAddress | 0~4294967295 | |
| 寄存器Property类型(Enum) | PropertyIds | 0~4194303 | 需要查询寄存器Property类型对照表T-2 |
| 要写入寄存器的值(object) | Value | 任意数值或者字符 | |
| 要写入寄存器的值类型(Enum) | ValueType | 1-5 | 需要查询Bacnet数据类型对照表T-3 |
## 响应结构说明
bacnet响应构成
| 声明 | 键值 | 示例 | 备注 |
| ------------------------- | ------------ | ------------------------------ | -------------------- |
| 功能码(string) | FunctionCode | OperateResponse | 固定值 |
| 是否成功(bool) | IsSuccess | true&false | |
| 请求体(Request) | Request | 参考2 Bacnet请求-Json结构说明 | |
| 数据体(IList) | ValueQuery | [ {  "Tag": 4,  "Value":12.0}] | |
| BacnetValue-Tag(Enum) | | 4 | 需要查询Bacnet数据类型对照表T-3 |
| BacnetValue-Value(object) | | 12.0 | |
| 失败原因(string) | FailedReason | - | 待开发 |
## Bacnet 读寄存器请求
示例:
```json
{
"FunctionCode":"OperateRequest",
"OperateCode":0,
"DeviceId":1,
"ObjectTypes":0,
"RegisterAddress":3000164,
"PropertyIds":85
}
```
## Bacnet 写寄存器请求
示例:
```json
{
"FunctionCode":"OperateRequest",
"OperateCode":1,
"DeviceId":1,
"ObjectTypes":2,
"RegisterAddress":3000637,
"PropertyIds":85,
"Value":10.0,
"ValueType":4
}
```
## Bacnet响应案例
查询&写入响应
```json
{
"FunctionCode": "OperateResponse",
"IsSuccess": true,
"Request": {
"FunctionCode": "OperateRequest",
"OperateCode": 0,
"DeviceId": 1,
"ObjectTypes": 0,
"RegisterAddress": 3000164,
"PropertyIds": 85,
"Value": null
},
"ValueQuery": [
{
"Tag": 4,
"Value": 12.0
}
],
"FailedReason": null
}
```
# 4.Bacnet 读多个寄存器
## http接口地址
/ReadMultiRegister
## 请求结构说明
Bacnet请求构成
| 声明 | 键值 | 示例 | 备注 |
| ------------------- | --------------- | ---------------- | ----------------------- |
| 功能码(string) | FunctionCode | ReadMultiRequest | 固定值 |
| 操作码(Enum-uint) | OperateCode | 3 | 需要查询操作码对照表T-0,固定值3 |
| 设备ID(uint) | DeviceId | 0~4294967295 | |
| 数组 | RegisterInfos | | |
| 数组对象结构 | - | - | - |
| 寄存器类型(Enum-uint) | ObjectTypes | 0~1024 | 需要查询寄存器类型对照表T-1 |
| 寄存器地址(uint) | RegisterAddress | 0~4294967295 | |
| 寄存器Property类型(Enum) | PropertyIds | 0~4194303 | 需要查询寄存器Property类型对照表T-2 |
## 响应结构说明
bacnet响应构成
| 声明 | 键值 | 示例 | 备注 |
| ------------------------- | --------------- | --------------------------- | -------------------- |
| 功能码(string) | FunctionCode | OperateResponse | 固定值 |
| 是否成功(bool) | IsSuccess | true&false | |
| 查询数组 | RegisterInfos | | |
| 数组对象结构 | - | - | - |
| 寄存器类型(Enum-uint) | ObjectTypes | 0~1024 | 需要查询寄存器类型对照表T-1 |
| 寄存器地址(uint) | RegisterAddress | 0~4294967295 | |
| 寄存器Property类型(Enum) | PropertyIds | 0~4194303 | |
| 数据体 | Value | {  "Tag": 4,  "Value":12.0} | |
| BacnetValue-Tag(Enum) | | 4 | 需要查询Bacnet数据类型对照表T-3 |
| BacnetValue-Value(object) | | 12.0 | |
| 失败原因(string) | FailedReason | - | 待开发 |
## Bacnet 读多个寄存器请求
示例:
```json
{
"FunctionCode":"ReadMultiRequest",
"OperateCode":3,
"DeviceId":1,
"RegisterInfos":[
{
"ObjectTypes":0,
"RegisterAddress":0,
"PropertyIds":85
},
{
"ObjectTypes":2,
"RegisterAddress":1,
"PropertyIds":85
},
{
"ObjectTypes":0,
"RegisterAddress":2,
"PropertyIds":85
}
]
}
```
## Bacnet读多个寄存器响应
示例:
```json
{
"FunctionCode": "ReadMultiResponse",
"IsSuccess": true,
"RegisterInfos": [
{
"ObjectTypes": 0,
"RegisterAddress": 0,
"PropertyIds": 85,
"IsSuccess": true,
"Value": {
"Tag": 4,
"Value": 12.0
}
},
{
"ObjectTypes": 2,
"RegisterAddress": 1,
"PropertyIds": 85,
"IsSuccess": true,
"Value": {
"Tag": 2,
"Value": 21
}
},
{
"ObjectTypes": 0,
"RegisterAddress": 2,
"PropertyIds": 85,
"IsSuccess": true,
"Value": {
"Tag": 4,
"Value": 5.0
}
}
],
"FailedReason": null
}
```
# 附表
## T-0 操作码对照表
| 操作类型 | 对应编号 | 说明 |
| -------------------- | ---- | ------------- |
| ReadPropertyRequest | 0 | 读单个寄存器请求 |
| WritePropertyRequest | 1 | 写单个寄存器请求 |
| WhoIsRequest | 2 | Who is 设备列表请求 |
| ReadMultiRequest | 3 | 读多个寄存器请求 |
原始数据
```csharp
public enum EGBacnetOperateCode{
ReadPropertyRequest = 0,
WritePropertyRequest = 1,
WhoIsRequest = 2
}
```
## T-1 寄存器类型对照表
因该对照表过于长,所以仅列出部分常用类型
| 寄存器类型 | 对应编号 | 说明 |
| ----------------- | ---- | ---- |
| Analog Value | 2 | 可读可写 |
| Analog Input | 0 | |
| Multi-state Value | 19 | |
| Binary Input | 3 | |
| Analog Output | 1 | |
| Binary Value | 5 | |
| Program | 16 | |
| Trendlog Multiple | 27 | |
原始数据
```csharp
public enum BacnetObjectTypes : uint
{
OBJECT_ANALOG_INPUT = 0u,
OBJECT_ANALOG_OUTPUT = 1u,
OBJECT_ANALOG_VALUE = 2u,
OBJECT_BINARY_INPUT = 3u,
OBJECT_BINARY_OUTPUT = 4u,
OBJECT_BINARY_VALUE = 5u,
OBJECT_CALENDAR = 6u,
OBJECT_COMMAND = 7u,
OBJECT_DEVICE = 8u,
OBJECT_EVENT_ENROLLMENT = 9u,
OBJECT_FILE = 10u,
OBJECT_GROUP = 11u,
OBJECT_LOOP = 12u,
OBJECT_MULTI_STATE_INPUT = 13u,
OBJECT_MULTI_STATE_OUTPUT = 14u,
OBJECT_NOTIFICATION_CLASS = 15u,
OBJECT_PROGRAM = 16u,
OBJECT_SCHEDULE = 17u,
OBJECT_AVERAGING = 18u,
OBJECT_MULTI_STATE_VALUE = 19u,
OBJECT_TRENDLOG = 20u,
OBJECT_LIFE_SAFETY_POINT = 21u,
OBJECT_LIFE_SAFETY_ZONE = 22u,
OBJECT_ACCUMULATOR = 23u,
OBJECT_PULSE_CONVERTER = 24u,
OBJECT_EVENT_LOG = 25u,
OBJECT_GLOBAL_GROUP = 26u,
OBJECT_TREND_LOG_MULTIPLE = 27u,
OBJECT_LOAD_CONTROL = 28u,
OBJECT_STRUCTURED_VIEW = 29u,
OBJECT_ACCESS_DOOR = 30u,
OBJECT_TIMER = 31u,
OBJECT_ACCESS_CREDENTIAL = 32u,
OBJECT_ACCESS_POINT = 33u,
OBJECT_ACCESS_RIGHTS = 34u,
OBJECT_ACCESS_USER = 35u,
OBJECT_ACCESS_ZONE = 36u,
OBJECT_CREDENTIAL_DATA_INPUT = 37u,
OBJECT_NETWORK_SECURITY = 38u,
OBJECT_BITSTRING_VALUE = 39u,
OBJECT_CHARACTERSTRING_VALUE = 40u,
OBJECT_DATE_PATTERN_VALUE = 41u,
OBJECT_DATE_VALUE = 42u,
OBJECT_DATETIME_PATTERN_VALUE = 43u,
OBJECT_DATETIME_VALUE = 44u,
OBJECT_INTEGER_VALUE = 45u,
OBJECT_LARGE_ANALOG_VALUE = 46u,
OBJECT_OCTETSTRING_VALUE = 47u,
OBJECT_POSITIVE_INTEGER_VALUE = 48u,
OBJECT_TIME_PATTERN_VALUE = 49u,
OBJECT_TIME_VALUE = 50u,
OBJECT_NOTIFICATION_FORWARDER = 51u,
OBJECT_ALERT_ENROLLMENT = 52u,
OBJECT_CHANNEL = 53u,
OBJECT_LIGHTING_OUTPUT = 54u,
OBJECT_BINARY_LIGHTING_OUTPUT = 55u,
OBJECT_PROPRIETARY_MIN = 128u,
OBJECT_PROPRIETARY_MAX = 1023u,
MAX_BACNET_OBJECT_TYPE = 1024u,
MAX_ASHRAE_OBJECT_TYPE = 56u
}
```
## T-2 寄存器Property类型对照表
因该对照表过于长,所以仅列出部分常用类型
| Property类型 | 对应编号 | 说明 |
| ------------------ | ---- | --- |
| description | 28 | |
| object-identifier | 75 | |
| object-type | 79 | |
| object-name | 77 | |
| event-state | 36 | |
| status-flags | 111 | |
| units | 117 | |
| out-of-service | 81 | |
| present-value | 85 | |
| relinquish-default | 104 | |
| priority-array | 87 | |
| number-of-states | 74 | |
| state-text | 110 | |
| program-state | 92 | |
| program-change | 0 | |
| buffer-size | 126 | |
| record-count | 141 | |
| total-record-count | 145 | |
| stop-when-full | 144 | |
| logging-type | 197 | |
| enable | 133 | |
原始数据
```csharp
public enum BacnetPropertyIds
{
PROP_ACKED_TRANSITIONS = 0,
PROP_ACK_REQUIRED = 1,
PROP_ACTION = 2,
PROP_ACTION_TEXT = 3,
PROP_ACTIVE_TEXT = 4,
PROP_ACTIVE_VT_SESSIONS = 5,
PROP_ALARM_VALUE = 6,
PROP_ALARM_VALUES = 7,
PROP_ALL = 8,
PROP_ALL_WRITES_SUCCESSFUL = 9,
PROP_APDU_SEGMENT_TIMEOUT = 10,
PROP_APDU_TIMEOUT = 11,
PROP_APPLICATION_SOFTWARE_VERSION = 12,
PROP_ARCHIVE = 13,
PROP_BIAS = 14,
PROP_CHANGE_OF_STATE_COUNT = 15,
PROP_CHANGE_OF_STATE_TIME = 16,
PROP_NOTIFICATION_CLASS = 17,
PROP_BLANK_1 = 18,
PROP_CONTROLLED_VARIABLE_REFERENCE = 19,
PROP_CONTROLLED_VARIABLE_UNITS = 20,
PROP_CONTROLLED_VARIABLE_VALUE = 21,
PROP_COV_INCREMENT = 22,
PROP_DATE_LIST = 23,
PROP_DAYLIGHT_SAVINGS_STATUS = 24,
PROP_DEADBAND = 25,
PROP_DERIVATIVE_CONSTANT = 26,
PROP_DERIVATIVE_CONSTANT_UNITS = 27,
PROP_DESCRIPTION = 28,
PROP_DESCRIPTION_OF_HALT = 29,
PROP_DEVICE_ADDRESS_BINDING = 30,
PROP_DEVICE_TYPE = 31,
PROP_EFFECTIVE_PERIOD = 32,
PROP_ELAPSED_ACTIVE_TIME = 33,
PROP_ERROR_LIMIT = 34,
PROP_EVENT_ENABLE = 35,
PROP_EVENT_STATE = 36,
PROP_EVENT_TYPE = 37,
PROP_EXCEPTION_SCHEDULE = 38,
PROP_FAULT_VALUES = 39,
PROP_FEEDBACK_VALUE = 40,
PROP_FILE_ACCESS_METHOD = 41,
PROP_FILE_SIZE = 42,
PROP_FILE_TYPE = 43,
PROP_FIRMWARE_REVISION = 44,
PROP_HIGH_LIMIT = 45,
PROP_INACTIVE_TEXT = 46,
PROP_IN_PROCESS = 47,
PROP_INSTANCE_OF = 48,
PROP_INTEGRAL_CONSTANT = 49,
PROP_INTEGRAL_CONSTANT_UNITS = 50,
PROP_ISSUE_CONFIRMED_NOTIFICATIONS = 51,
PROP_LIMIT_ENABLE = 52,
PROP_LIST_OF_GROUP_MEMBERS = 53,
PROP_LIST_OF_OBJECT_PROPERTY_REFERENCES = 54,
PROP_LIST_OF_SESSION_KEYS = 55,
PROP_LOCAL_DATE = 56,
PROP_LOCAL_TIME = 57,
PROP_LOCATION = 58,
PROP_LOW_LIMIT = 59,
PROP_MANIPULATED_VARIABLE_REFERENCE = 60,
PROP_MAXIMUM_OUTPUT = 61,
PROP_MAX_APDU_LENGTH_ACCEPTED = 62,
PROP_MAX_INFO_FRAMES = 63,
PROP_MAX_MASTER = 64,
PROP_MAX_PRES_VALUE = 65,
PROP_MINIMUM_OFF_TIME = 66,
PROP_MINIMUM_ON_TIME = 67,
PROP_MINIMUM_OUTPUT = 68,
PROP_MIN_PRES_VALUE = 69,
PROP_MODEL_NAME = 70,
PROP_MODIFICATION_DATE = 71,
PROP_NOTIFY_TYPE = 72,
PROP_NUMBER_OF_APDU_RETRIES = 73,
PROP_NUMBER_OF_STATES = 74,
PROP_OBJECT_IDENTIFIER = 75,
PROP_OBJECT_LIST = 76,
PROP_OBJECT_NAME = 77,
PROP_OBJECT_PROPERTY_REFERENCE = 78,
PROP_OBJECT_TYPE = 79,
PROP_OPTIONAL = 80,
PROP_OUT_OF_SERVICE = 81,
PROP_OUTPUT_UNITS = 82,
PROP_EVENT_PARAMETERS = 83,
PROP_POLARITY = 84,
PROP_PRESENT_VALUE = 85,
PROP_PRIORITY = 86,
PROP_PRIORITY_ARRAY = 87,
PROP_PRIORITY_FOR_WRITING = 88,
PROP_PROCESS_IDENTIFIER = 89,
PROP_PROGRAM_CHANGE = 90,
PROP_PROGRAM_LOCATION = 91,
PROP_PROGRAM_STATE = 92,
PROP_PROPORTIONAL_CONSTANT = 93,
PROP_PROPORTIONAL_CONSTANT_UNITS = 94,
PROP_PROTOCOL_CONFORMANCE_CLASS = 95,
PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED = 96,
PROP_PROTOCOL_SERVICES_SUPPORTED = 97,
PROP_PROTOCOL_VERSION = 98,
PROP_READ_ONLY = 99,
PROP_REASON_FOR_HALT = 100,
PROP_RECIPIENT = 101,
PROP_RECIPIENT_LIST = 102,
PROP_RELIABILITY = 103,
PROP_RELINQUISH_DEFAULT = 104,
PROP_REQUIRED = 105,
PROP_RESOLUTION = 106,
PROP_SEGMENTATION_SUPPORTED = 107,
PROP_SETPOINT = 108,
PROP_SETPOINT_REFERENCE = 109,
PROP_STATE_TEXT = 110,
PROP_STATUS_FLAGS = 111,
PROP_SYSTEM_STATUS = 112,
PROP_TIME_DELAY = 113,
PROP_TIME_OF_ACTIVE_TIME_RESET = 114,
PROP_TIME_OF_STATE_COUNT_RESET = 115,
PROP_TIME_SYNCHRONIZATION_RECIPIENTS = 116,
PROP_UNITS = 117,
PROP_UPDATE_INTERVAL = 118,
PROP_UTC_OFFSET = 119,
PROP_VENDOR_IDENTIFIER = 120,
PROP_VENDOR_NAME = 121,
PROP_VT_CLASSES_SUPPORTED = 122,
PROP_WEEKLY_SCHEDULE = 123,
PROP_ATTEMPTED_SAMPLES = 124,
PROP_AVERAGE_VALUE = 125,
PROP_BUFFER_SIZE = 126,
PROP_CLIENT_COV_INCREMENT = 127,
PROP_COV_RESUBSCRIPTION_INTERVAL = 128,
PROP_CURRENT_NOTIFY_TIME = 129,
PROP_EVENT_TIME_STAMPS = 130,
PROP_LOG_BUFFER = 131,
PROP_LOG_DEVICE_OBJECT_PROPERTY = 132,
PROP_ENABLE = 133,
PROP_LOG_INTERVAL = 134,
PROP_MAXIMUM_VALUE = 135,
PROP_MINIMUM_VALUE = 136,
PROP_NOTIFICATION_THRESHOLD = 137,
PROP_PREVIOUS_NOTIFY_TIME = 138,
PROP_PROTOCOL_REVISION = 139,
PROP_RECORDS_SINCE_NOTIFICATION = 140,
PROP_RECORD_COUNT = 141,
PROP_START_TIME = 142,
PROP_STOP_TIME = 143,
PROP_STOP_WHEN_FULL = 144,
PROP_TOTAL_RECORD_COUNT = 145,
PROP_VALID_SAMPLES = 146,
PROP_WINDOW_INTERVAL = 147,
PROP_WINDOW_SAMPLES = 148,
PROP_MAXIMUM_VALUE_TIMESTAMP = 149,
PROP_MINIMUM_VALUE_TIMESTAMP = 150,
PROP_VARIANCE_VALUE = 151,
PROP_ACTIVE_COV_SUBSCRIPTIONS = 152,
PROP_BACKUP_FAILURE_TIMEOUT = 153,
PROP_CONFIGURATION_FILES = 154,
PROP_DATABASE_REVISION = 155,
PROP_DIRECT_READING = 156,
PROP_LAST_RESTORE_TIME = 157,
PROP_MAINTENANCE_REQUIRED = 158,
PROP_MEMBER_OF = 159,
PROP_MODE = 160,
PROP_OPERATION_EXPECTED = 161,
PROP_SETTING = 162,
PROP_SILENCED = 163,
PROP_TRACKING_VALUE = 164,
PROP_ZONE_MEMBERS = 165,
PROP_LIFE_SAFETY_ALARM_VALUES = 166,
PROP_MAX_SEGMENTS_ACCEPTED = 167,
PROP_PROFILE_NAME = 168,
PROP_AUTO_SLAVE_DISCOVERY = 169,
PROP_MANUAL_SLAVE_ADDRESS_BINDING = 170,
PROP_SLAVE_ADDRESS_BINDING = 171,
PROP_SLAVE_PROXY_ENABLE = 172,
PROP_LAST_NOTIFY_RECORD = 173,
PROP_SCHEDULE_DEFAULT = 174,
PROP_ACCEPTED_MODES = 175,
PROP_ADJUST_VALUE = 176,
PROP_COUNT = 177,
PROP_COUNT_BEFORE_CHANGE = 178,
PROP_COUNT_CHANGE_TIME = 179,
PROP_COV_PERIOD = 180,
PROP_INPUT_REFERENCE = 181,
PROP_LIMIT_MONITORING_INTERVAL = 182,
PROP_LOGGING_OBJECT = 183,
PROP_LOGGING_RECORD = 184,
PROP_PRESCALE = 185,
PROP_PULSE_RATE = 186,
PROP_SCALE = 187,
PROP_SCALE_FACTOR = 188,
PROP_UPDATE_TIME = 189,
PROP_VALUE_BEFORE_CHANGE = 190,
PROP_VALUE_SET = 191,
PROP_VALUE_CHANGE_TIME = 192,
PROP_ALIGN_INTERVALS = 193,
PROP_INTERVAL_OFFSET = 195,
PROP_LAST_RESTART_REASON = 196,
PROP_LOGGING_TYPE = 197,
PROP_RESTART_NOTIFICATION_RECIPIENTS = 202,
PROP_TIME_OF_DEVICE_RESTART = 203,
PROP_TIME_SYNCHRONIZATION_INTERVAL = 204,
PROP_TRIGGER = 205,
PROP_UTC_TIME_SYNCHRONIZATION_RECIPIENTS = 206,
PROP_NODE_SUBTYPE = 207,
PROP_NODE_TYPE = 208,
PROP_STRUCTURED_OBJECT_LIST = 209,
PROP_SUBORDINATE_ANNOTATIONS = 210,
PROP_SUBORDINATE_LIST = 211,
PROP_ACTUAL_SHED_LEVEL = 212,
PROP_DUTY_WINDOW = 213,
PROP_EXPECTED_SHED_LEVEL = 214,
PROP_FULL_DUTY_BASELINE = 215,
PROP_REQUESTED_SHED_LEVEL = 218,
PROP_SHED_DURATION = 219,
PROP_SHED_LEVEL_DESCRIPTIONS = 220,
PROP_SHED_LEVELS = 221,
PROP_STATE_DESCRIPTION = 222,
PROP_DOOR_ALARM_STATE = 226,
PROP_DOOR_EXTENDED_PULSE_TIME = 227,
PROP_DOOR_MEMBERS = 228,
PROP_DOOR_OPEN_TOO_LONG_TIME = 229,
PROP_DOOR_PULSE_TIME = 230,
PROP_DOOR_STATUS = 231,
PROP_DOOR_UNLOCK_DELAY_TIME = 232,
PROP_LOCK_STATUS = 233,
PROP_MASKED_ALARM_VALUES = 234,
PROP_SECURED_STATUS = 235,
PROP_ABSENTEE_LIMIT = 244,
PROP_ACCESS_ALARM_EVENTS = 245,
PROP_ACCESS_DOORS = 246,
PROP_ACCESS_EVENT = 247,
PROP_ACCESS_EVENT_AUTHENTICATION_FACTOR = 248,
PROP_ACCESS_EVENT_CREDENTIAL = 249,
PROP_ACCESS_EVENT_TIME = 250,
PROP_ACCESS_TRANSACTION_EVENTS = 251,
PROP_ACCOMPANIMENT = 252,
PROP_ACCOMPANIMENT_TIME = 253,
PROP_ACTIVATION_TIME = 254,
PROP_ACTIVE_AUTHENTICATION_POLICY = 255,
PROP_ASSIGNED_ACCESS_RIGHTS = 256,
PROP_AUTHENTICATION_FACTORS = 257,
PROP_AUTHENTICATION_POLICY_LIST = 258,
PROP_AUTHENTICATION_POLICY_NAMES = 259,
PROP_AUTHENTICATION_STATUS = 260,
PROP_AUTHORIZATION_MODE = 261,
PROP_BELONGS_TO = 262,
PROP_CREDENTIAL_DISABLE = 263,
PROP_CREDENTIAL_STATUS = 264,
PROP_CREDENTIALS = 265,
PROP_CREDENTIALS_IN_ZONE = 266,
PROP_DAYS_REMAINING = 267,
PROP_ENTRY_POINTS = 268,
PROP_EXIT_POINTS = 269,
PROP_EXPIRY_TIME = 270,
PROP_EXTENDED_TIME_ENABLE = 271,
PROP_FAILED_ATTEMPT_EVENTS = 272,
PROP_FAILED_ATTEMPTS = 273,
PROP_FAILED_ATTEMPTS_TIME = 274,
PROP_LAST_ACCESS_EVENT = 275,
PROP_LAST_ACCESS_POINT = 276,
PROP_LAST_CREDENTIAL_ADDED = 277,
PROP_LAST_CREDENTIAL_ADDED_TIME = 278,
PROP_LAST_CREDENTIAL_REMOVED = 279,
PROP_LAST_CREDENTIAL_REMOVED_TIME = 280,
PROP_LAST_USE_TIME = 281,
PROP_LOCKOUT = 282,
PROP_LOCKOUT_RELINQUISH_TIME = 283,
PROP_MASTER_EXEMPTION = 284,
PROP_MAX_FAILED_ATTEMPTS = 285,
PROP_MEMBERS = 286,
PROP_MUSTER_POINT = 287,
PROP_NEGATIVE_ACCESS_RULES = 288,
PROP_NUMBER_OF_AUTHENTICATION_POLICIES = 289,
PROP_OCCUPANCY_COUNT = 290,
PROP_OCCUPANCY_COUNT_ADJUST = 291,
PROP_OCCUPANCY_COUNT_ENABLE = 292,
PROP_OCCUPANCY_EXEMPTION = 293,
PROP_OCCUPANCY_LOWER_LIMIT = 294,
PROP_OCCUPANCY_LOWER_LIMIT_ENFORCED = 295,
PROP_OCCUPANCY_STATE = 296,
PROP_OCCUPANCY_UPPER_LIMIT = 297,
PROP_OCCUPANCY_UPPER_LIMIT_ENFORCED = 298,
PROP_PASSBACK_EXEMPTION = 299,
PROP_PASSBACK_MODE = 300,
PROP_PASSBACK_TIMEOUT = 301,
PROP_POSITIVE_ACCESS_RULES = 302,
PROP_REASON_FOR_DISABLE = 303,
PROP_SUPPORTED_FORMATS = 304,
PROP_SUPPORTED_FORMAT_CLASSES = 305,
PROP_THREAT_AUTHORITY = 306,
PROP_THREAT_LEVEL = 307,
PROP_TRACE_FLAG = 308,
PROP_TRANSACTION_NOTIFICATION_CLASS = 309,
PROP_USER_EXTERNAL_IDENTIFIER = 310,
PROP_USER_INFORMATION_REFERENCE = 311,
PROP_USER_NAME = 317,
PROP_USER_TYPE = 318,
PROP_USES_REMAINING = 319,
PROP_ZONE_FROM = 320,
PROP_ZONE_TO = 321,
PROP_ACCESS_EVENT_TAG = 322,
PROP_GLOBAL_IDENTIFIER = 323,
PROP_VERIFICATION_TIME = 326,
PROP_BASE_DEVICE_SECURITY_POLICY = 327,
PROP_DISTRIBUTION_KEY_REVISION = 328,
PROP_DO_NOT_HIDE = 329,
PROP_KEY_SETS = 330,
PROP_LAST_KEY_SERVER = 331,
PROP_NETWORK_ACCESS_SECURITY_POLICIES = 332,
PROP_PACKET_REORDER_TIME = 333,
PROP_SECURITY_PDU_TIMEOUT = 334,
PROP_SECURITY_TIME_WINDOW = 335,
PROP_SUPPORTED_SECURITY_ALGORITHM = 336,
PROP_UPDATE_KEY_SET_TIMEOUT = 337,
PROP_BACKUP_AND_RESTORE_STATE = 338,
PROP_BACKUP_PREPARATION_TIME = 339,
PROP_RESTORE_COMPLETION_TIME = 340,
PROP_RESTORE_PREPARATION_TIME = 341,
PROP_BIT_MASK = 342,
PROP_BIT_TEXT = 343,
PROP_IS_UTC = 344,
PROP_GROUP_MEMBERS = 345,
PROP_GROUP_MEMBER_NAMES = 346,
PROP_MEMBER_STATUS_FLAGS = 347,
PROP_REQUESTED_UPDATE_INTERVAL = 348,
PROP_COVU_PERIOD = 349,
PROP_COVU_RECIPIENTS = 350,
PROP_EVENT_MESSAGE_TEXTS = 351,
PROP_EVENT_MESSAGE_TEXTS_CONFIG = 352,
PROP_EVENT_DETECTION_ENABLE = 353,
PROP_EVENT_ALGORITHM_INHIBIT = 354,
PROP_EVENT_ALGORITHM_INHIBIT_REF = 355,
PROP_TIME_DELAY_NORMAL = 356,
PROP_RELIABILITY_EVALUATION_INHIBIT = 357,
PROP_FAULT_PARAMETERS = 358,
PROP_FAULT_TYPE = 359,
PROP_LOCAL_FORWARDING_ONLY = 360,
PROP_PROCESS_IDENTIFIER_FILTER = 361,
PROP_SUBSCRIBED_RECIPIENTS = 362,
PROP_PORT_FILTER = 363,
PROP_AUTHORIZATION_EXEMPTIONS = 364,
PROP_ALLOW_GROUP_DELAY_INHIBIT = 365,
PROP_CHANNEL_NUMBER = 366,
PROP_CONTROL_GROUPS = 367,
PROP_EXECUTION_DELAY = 368,
PROP_LAST_PRIORITY = 369,
PROP_WRITE_STATUS = 370,
PROP_PROPERTY_LIST = 371,
PROP_SERIAL_NUMBER = 372,
PROP_BLINK_WARN_ENABLE = 373,
PROP_DEFAULT_FADE_TIME = 374,
PROP_DEFAULT_RAMP_RATE = 375,
PROP_DEFAULT_STEP_INCREMENT = 376,
PROP_EGRESS_TIME = 377,
PROP_IN_PROGRESS = 378,
PROP_INSTANTANEOUS_POWER = 379,
PROP_LIGHTING_COMMAND = 380,
PROP_LIGHTING_COMMAND_DEFAULT_PRIORITY = 381,
PROP_MAX_ACTUAL_VALUE = 382,
PROP_MIN_ACTUAL_VALUE = 383,
PROP_POWER = 384,
PROP_TRANSITION = 385,
PROP_EGRESS_ACTIVE = 386,
PROP_INTERFACE_VALUE = 387,
PROP_FAULT_HIGH_LIMIT = 388,
PROP_FAULT_LOW_LIMIT = 389,
PROP_LOW_DIFF_LIMIT = 390,
PROP_STRIKE_COUNT = 391,
PROP_TIME_OF_STRIKE_COUNT_RESET = 392,
PROP_DEFAULT_TIMEOUT = 393,
PROP_INITIAL_TIMEOUT = 394,
PROP_LAST_STATE_CHANGE = 395,
PROP_STATE_CHANGE_VALUES = 396,
PROP_TIMER_RUNNING = 397,
PROP_TIMER_STATE = 398,
PROP_COMMAND_TIME_ARRAY = 430,
PROP_CURRENT_COMMAND_PRIORITY = 431,
PROP_LAST_COMMAND_TIME = 432,
PROP_VALUE_SOURCE = 433,
PROP_VALUE_SOURCE_ARRAY = 434,
MAX_BACNET_PROPERTY_ID = 4194303
}
```
## T-3 Bacnet数据类型对照表
因该对照表过于长,所以仅列出部分常用类型
| Property类型 | 对应编号 | 说明 |
| ---------------- | ---- | ----- |
| NULL | 0 | 空 |
| BOOLEAN | 1 | 布尔值 |
| UNSIGNED_INT | 2 | 无符号整型 |
| SIGNED_INT | 3 | 整形 |
| REAL | 4 | 实数浮点 |
| DOUBLE | 5 | 双精度浮点 |
| OCTET_STRING | 6 | |
| CHARACTER_STRING | 7 | |
| BIT_STRING | 8 | |
| ENUMERATED | 9 | |
| DATE | 10 | 日期 |
| TIME | 11 | 时间 |
原始数据:
```csharp
public enum BacnetApplicationTags
{
BACNET_APPLICATION_TAG_NULL,
BACNET_APPLICATION_TAG_BOOLEAN,
BACNET_APPLICATION_TAG_UNSIGNED_INT,
BACNET_APPLICATION_TAG_SIGNED_INT,
BACNET_APPLICATION_TAG_REAL,
BACNET_APPLICATION_TAG_DOUBLE,
BACNET_APPLICATION_TAG_OCTET_STRING,
BACNET_APPLICATION_TAG_CHARACTER_STRING,
BACNET_APPLICATION_TAG_BIT_STRING,
BACNET_APPLICATION_TAG_ENUMERATED,
BACNET_APPLICATION_TAG_DATE,
BACNET_APPLICATION_TAG_TIME,
BACNET_APPLICATION_TAG_OBJECT_ID,
BACNET_APPLICATION_TAG_RESERVE1,
BACNET_APPLICATION_TAG_RESERVE2,
BACNET_APPLICATION_TAG_RESERVE3,
MAX_BACNET_APPLICATION_TAG,
BACNET_APPLICATION_TAG_EMPTYLIST,
BACNET_APPLICATION_TAG_WEEKNDAY,
BACNET_APPLICATION_TAG_DATERANGE,
BACNET_APPLICATION_TAG_DATETIME,
BACNET_APPLICATION_TAG_TIMESTAMP,
BACNET_APPLICATION_TAG_ERROR,
BACNET_APPLICATION_TAG_DEVICE_OBJECT_PROPERTY_REFERENCE,
BACNET_APPLICATION_TAG_DEVICE_OBJECT_REFERENCE,
BACNET_APPLICATION_TAG_OBJECT_PROPERTY_REFERENCE,
BACNET_APPLICATION_TAG_DESTINATION,
BACNET_APPLICATION_TAG_RECIPIENT,
BACNET_APPLICATION_TAG_COV_SUBSCRIPTION,
BACNET_APPLICATION_TAG_CALENDAR_ENTRY,
BACNET_APPLICATION_TAG_WEEKLY_SCHEDULE,
BACNET_APPLICATION_TAG_SPECIAL_EVENT,
BACNET_APPLICATION_TAG_READ_ACCESS_SPECIFICATION,
BACNET_APPLICATION_TAG_READ_ACCESS_RESULT,
BACNET_APPLICATION_TAG_LIGHTING_COMMAND,
BACNET_APPLICATION_TAG_CONTEXT_SPECIFIC_DECODED,
BACNET_APPLICATION_TAG_CONTEXT_SPECIFIC_ENCODED,
BACNET_APPLICATION_TAG_LOG_RECORD
}
```

12
Example/Gateway/ModbusGateway.tscn

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
[gd_scene load_steps=2 format=3 uid="uid://c8vhi8ojdtjnb"]
[ext_resource type="Script" path="res://Example/Gateway/Script/View/ViewModbusGateway.cs" id="1_34uaa"]
[node name="ModbusGateway" type="Control"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_34uaa")

13
Example/Gateway/Script/Data/DataBacNetGatewaySetting.cs

@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
using System.Collections.Generic;
using System.IO.BACnet;
namespace EGFramework.Examples.Gateway
{
public class DataBacnetGatewaySetting {
public string MqttHost { set; get; }
public string HttpServerPrefix { set; get; }
public string RequestTheme { set; get; }
public string ResponseTheme { set; get; }
}
}

31
Example/Gateway/Script/Data/DataModbusGatewaySettings.cs

@ -0,0 +1,31 @@ @@ -0,0 +1,31 @@
using System.Collections.Generic;
namespace EGFramework.Examples.Gateway{
public class DataModbusGatewaySetting{
public float Delay { set; get; }
public List<DataModbus485Device> Devices485 = new List<DataModbus485Device>();
public List<DataModbusTCPDevice> DevicesTCP = new List<DataModbusTCPDevice>();
}
public class DataModbus485Device{
public string SerialPort { set; get; }
public byte Address { set; get; }
public int BaudRate { set; get; }
public Dictionary<string,DataModbusRegister> Registers = new Dictionary<string, DataModbusRegister>();
}
public class DataModbusTCPDevice{
public string Host { set; get; }
public int Port { set; get; }
public byte Address { set; get; }
public Dictionary<string,DataModbusRegister> Registers = new Dictionary<string, DataModbusRegister>();
}
public class DataModbusRegister{
public short Address { set; get; }
public ModbusRegisterType RegisterType { set; get; } = ModbusRegisterType.HoldingRegister;
public string Info { set; get; }
public string Unit { set; get; }
}
}

16
Example/Gateway/Script/Data/DataTcpGatewaySetting.cs

@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
using System.Collections.Generic;
namespace EGFramework.Examples.Gateway{
public class DataTcpGatewaySetting{
public List<DataTcpGatewayDevice> DataTcpGatewayDevices = new List<DataTcpGatewayDevice>();
}
public class DataTcpGatewayDevice{
public string Host { set; get; }
public int Port { set; get; }
public string MqttHost { set; get; }
public string RequestTheme { set; get; }
public string ResponseTheme { set; get; }
}
}

6
Example/Gateway/Script/Interface/IGateway.cs

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
namespace EGFramework.Examples.Gateway
{
public interface IGateway{
public void InitGateway();
}
}

73
Example/Gateway/Script/View/ViewBacNetGateway.cs

@ -0,0 +1,73 @@ @@ -0,0 +1,73 @@
using Godot;
using System;
using System.Collections.Generic;
using System.IO.BACnet;
using System.Threading;
using System.Threading.Tasks;
namespace EGFramework.Examples.Gateway{
public partial class ViewBacNetGateway : Control,IGateway,IEGFramework
{
public DataBacnetGatewaySetting DataBacnetGatewaySetting { set; get; }
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
if(this.Visible){
InitGateway();
}
}
public void InitGateway()
{
if(this.EGSave().GetDataByFile<DataBacnetGatewaySetting>() == null){
DataBacnetGatewaySetting = new DataBacnetGatewaySetting(){
MqttHost = "192.168.1.220",
HttpServerPrefix = "http://127.0.0.1:5000/",
ResponseTheme = "/LocalBacnetResponse",
RequestTheme = "/LocalBacnetRequest"
};
this.EGSave().SetDataToFile(DataBacnetGatewaySetting);
}else{
DataBacnetGatewaySetting = this.EGSave().GetDataByFile<DataBacnetGatewaySetting>();
}
this.EGEnabledProtocolTool<EGBacnet>();
this.EGEnabledProtocolTool<EGMqtt>();
InitMqttClient(DataBacnetGatewaySetting);
this.EGOnMessage<EGBacnetRequest>();
this.EGOnMessage<EGBacnetResponse>();
this.EGOnMessage<EGBacnetWhoIsResponse>();
}
public void InitMqttClient(DataBacnetGatewaySetting settings){
this.EGMqtt().OnMqttConnect.Register(e=>{
if(e == settings.MqttHost){
this.EGMqtt().SubScribeTheme(settings.MqttHost,settings.RequestTheme);
// byte[] testData = {0x3A,0x55};
// this.EGMqtt().PublishTheme("192.168.1.220","test",testData);
}
});
this.EGRegisterMessageEvent<EGBacnetRequest>((e,sender,protocol)=>{
GD.Print("Sender:"+sender);
if(protocol == ProtocolType.MQTTClient && sender == settings.MqttHost+"|"+settings.RequestTheme){
GD.Print("MQTT Received->BACnet:"+e.ToProtocolData());
this.EGSendMessage(e,e.DeviceId.ToString(),ProtocolType.Bacnet);
}
});
this.EGRegisterMessageEvent<EGBacnetResponse>((e,sender,protocol)=>{
if(protocol == ProtocolType.Bacnet){
GD.Print("BACnet Received->MQTT:"+e.ToProtocolData());
this.EGSendMessage(e,settings.MqttHost+"|"+settings.ResponseTheme,ProtocolType.MQTTClient);
}
});
this.EGRegisterMessageEvent<EGBacnetWhoIsResponse>((e,sender,protocol)=>{;
if(protocol == ProtocolType.Bacnet){
GD.Print("BACnet Who Is Received->MQTT:"+e.ToProtocolData());
this.EGSendMessage(e,settings.MqttHost+"|"+settings.ResponseTheme,ProtocolType.MQTTClient);
}
});
this.EGMqtt().ConnectMQTTServer(settings.MqttHost);
GD.Print("Init Over");
}
}
}

57
Example/Gateway/Script/View/ViewBacnetHttpServer.cs

@ -0,0 +1,57 @@ @@ -0,0 +1,57 @@
using EGFramework;
using Godot;
using System;
namespace EGFramework.Examples.Gateway{
public partial class ViewBacnetHttpServer : Control,IGateway,IEGFramework
{
public DataBacnetGatewaySetting DataBacnetGatewaySetting { set; get; }
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
if(this.Visible){
InitGateway();
}
}
public void InitGateway(){
if(this.EGSave().GetDataByFile<DataBacnetGatewaySetting>() == null){
DataBacnetGatewaySetting = new DataBacnetGatewaySetting(){
MqttHost = "192.168.1.220",
HttpServerPrefix = "http://127.0.0.1:5000/",
ResponseTheme = "/LocalBacnetResponse",
RequestTheme = "/LocalBacnetRequest"
};
this.EGSave().SetDataToFile(DataBacnetGatewaySetting);
}else{
DataBacnetGatewaySetting = this.EGSave().GetDataByFile<DataBacnetGatewaySetting>();
}
this.EGEnabledProtocolTool<EGBacnet>();
this.EGHttpServerListen(DataBacnetGatewaySetting.HttpServerPrefix+"WhoIs/",requestMsg=>{
GD.Print("---WhoIsRequest---");
return this.EGBacnet().WhoIs();
});
this.EGHttpServerListen(DataBacnetGatewaySetting.HttpServerPrefix+"ReadRegisterProperty/",requestMsg=>{
GD.Print("---ReadRegisterPropertyRequest---");
EGBacnetRequest bacnetRequest = new EGBacnetRequest();
bacnetRequest.TrySetData(requestMsg.stringData,null);
EGBacnetResponse response = this.EGBacnet().ReadRegisterProperty(bacnetRequest);
return response;
});
this.EGHttpServerListen(DataBacnetGatewaySetting.HttpServerPrefix+"ReadMultiRegister/",requestMsg=>{
GD.Print("---ReadMultiRequest---");
EGBacnetRequestReadMulti bacnetRequest = new EGBacnetRequestReadMulti();
bacnetRequest.TrySetData(requestMsg.stringData,null);
GD.Print("Get "+bacnetRequest.RegisterInfos.Count+" Values Request");
EGBacnetResponseReadMulti response = this.EGBacnet().ReadRegisterMulti(bacnetRequest);
return response;
});
this.EGHttpServerListen(DataBacnetGatewaySetting.HttpServerPrefix+"WriteRegisterProperty/",requestMsg=>{
GD.Print("---WriteRegisterPropertyRequest---");
EGBacnetRequest bacnetRequest = new EGBacnetRequest();
bacnetRequest.TrySetData(requestMsg.stringData,null);
EGBacnetResponse response = this.EGBacnet().WriteRegisterProperty(bacnetRequest);
return response;
});
}
}
}

23
Example/Gateway/Script/View/ViewGateway.cs

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
using Godot;
using EGFramework;
using System;
using System.Threading.Tasks;
namespace EGFramework.Examples.Gateway{
public partial class ViewGateway : Control,IEGFramework
{
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
}
// Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta)
{
}
public override void _ExitTree()
{
}
}
}

69
Example/Gateway/Script/View/ViewModbusGateway.cs

@ -0,0 +1,69 @@ @@ -0,0 +1,69 @@
using EGFramework;
using EGFramework.Examples.Gateway;
using Godot;
using System;
namespace EGFramework.Examples.Gateway{
public partial class ViewModbusGateway : Control,IEGFramework,IGateway
{
public DataModbusGatewaySetting Setting { set; get; }
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
this.EGEnabledProtocolTool<EGSerialPort>();
this.EGEnabledProtocolTool<EGTCPClient>();
this.EGSerialPort().SetBaudRate(9600);
ReadTest();
ReadTest2();
ReadTest3();
ReadTest3();
ReadTest2();
ReadTest();
}
// Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta)
{
}
public void InitGateway()
{
if(this.EGSave().GetDataByFile<DataModbusGatewaySetting>() == null){
Setting = new DataModbusGatewaySetting();
this.EGSave().SetDataToFile(Setting);
}else{
Setting = this.EGSave().GetDataByFile<DataModbusGatewaySetting>();
}
this.EGEnabledProtocolTool<EGTCPClient>();
this.EGEnabledProtocolTool<EGSerialPort>();
//this.EGOnMessage<GateWayMessage>();
}
public async void ReadTest(){
ModbusRTU_Response? result = await this.EGModbus().ReadRTUAsync(ModbusRegisterType.HoldingRegister,"COM4",0x01,0x00,0x01);
if(result != null){
GD.Print("Register[0]"+((ModbusRTU_Response)result).HoldingRegister[0]);
}else{
GD.Print("Timeout!");
}
}
public async void ReadTest2(){
ModbusRTU_Response? result2 = await this.EGModbus().ReadRTUAsync(ModbusRegisterType.HoldingRegister,"COM4",0x01,0x01,0x01);
if(result2 != null){
GD.Print("Register[1]"+((ModbusRTU_Response)result2).HoldingRegister[0]);
}else{
GD.Print("Timeout!");
}
}
public async void ReadTest3(){
ModbusRTU_Response? result3 = await this.EGModbus().ReadRTUAsync(ModbusRegisterType.HoldingRegister,"COM4",0x01,0x10,0x01);
if(result3 != null){
GD.Print("Register[2]"+((ModbusRTU_Response)result3).HoldingRegister[0]);
}else{
GD.Print("Timeout!");
}
}
}
}

109
Example/Gateway/Script/View/ViewTcpGateway.cs

@ -0,0 +1,109 @@ @@ -0,0 +1,109 @@
using Godot;
using System;
using System.Net.Sockets;
namespace EGFramework.Examples.Gateway{
public partial class ViewTcpGateway : Control,IEGFramework,IGateway
{
public DataTcpGatewaySetting DataTcpGatewaySetting { set; get; }
public override void _Ready()
{
if(this.Visible){
InitGateway();
}
}
public void InitGateway(){
if(this.EGSave().GetDataByFile<DataTcpGatewaySetting>() == null){
DataTcpGatewaySetting = new DataTcpGatewaySetting();
DataTcpGatewaySetting.DataTcpGatewayDevices.Add(new DataTcpGatewayDevice(){
Host = "127.0.0.1",
Port = 8234,
MqttHost = "192.168.1.220",
ResponseTheme = "/LocalTCPResponse",
RequestTheme = "/LocalTCPRequest"
});
this.EGSave().SetDataToFile(DataTcpGatewaySetting);
}else{
DataTcpGatewaySetting = this.EGSave().GetDataByFile<DataTcpGatewaySetting>();
}
this.EGEnabledProtocolTool<EGTCPClient>();
this.EGEnabledProtocolTool<EGMqtt>();
InitMqttClient(DataTcpGatewaySetting);
this.EGOnMessage<GateWayMessage>();
}
public void InitMqttClient(DataTcpGatewaySetting settings){
foreach(DataTcpGatewayDevice deviceSetting in settings.DataTcpGatewayDevices){
InitOneMqttClient(deviceSetting);
}
}
public async void InitOneMqttClient(DataTcpGatewayDevice deviceSetting){
this.EGMqtt().OnMqttConnect.Register(e=>{
if(e == deviceSetting.MqttHost){
this.EGMqtt().SubScribeTheme(deviceSetting.MqttHost,deviceSetting.RequestTheme);
// byte[] testData = {0x3A,0x55};
// this.EGMqtt().PublishTheme("192.168.1.220","test",testData);
}
});
this.EGRegisterMessageEvent<GateWayMessage>((e,sender,protocol)=>{
GD.Print("Sender:"+sender);
if(protocol == ProtocolType.MQTTClient && sender == deviceSetting.MqttHost+"|"+deviceSetting.RequestTheme){
GD.Print("MQTT Received->TCP:"+e.DataBytes.ToStringByHex());
this.EGSendMessage(new GateWayMessage(e.DataBytes),deviceSetting.Host+":"+deviceSetting.Port,ProtocolType.TCPClient);
}
});
this.EGRegisterMessageEvent<GateWayMessage>((e,sender,protocol)=>{
if(protocol == ProtocolType.TCPClient && sender == deviceSetting.Host+":"+deviceSetting.Port){
GD.Print("TCP Received->MQTT:"+e.DataBytes.ToStringByHex());
this.EGSendMessage(new GateWayMessage(e.DataBytes),deviceSetting.MqttHost+"|"+deviceSetting.ResponseTheme,ProtocolType.MQTTClient);
}
});
this.EGMqtt().ConnectMQTTServer(deviceSetting.MqttHost);
await this.EGTCPClient().ConnectTCP(deviceSetting.Host,deviceSetting.Port);
GD.Print("Init Over");
}
public override void _Process(double delta)
{
}
}
public class GateWayMessage : IRequest, IResponse
{
public byte[] DataBytes;
public string DataString;
public GateWayMessage(){
}
public GateWayMessage(byte[] bytes){
this.DataBytes = bytes;
}
public byte[] ToProtocolByteData()
{
return DataBytes;
}
public string ToProtocolData()
{
return DataString;
}
public bool TrySetData(string protocolData, byte[] protocolBytes)
{
try
{
this.DataBytes = protocolBytes;
this.DataString = protocolData;
return true;
}
catch (System.Exception e)
{
GD.PrintErr(e);
return false;
throw;
}
}
}
}

154
Example/ModbusDebugTool/Component/modbus_item.tscn

@ -0,0 +1,154 @@ @@ -0,0 +1,154 @@
[gd_scene load_steps=3 format=3 uid="uid://c8kk7iegicmdr"]
[ext_resource type="Script" path="res://Example/ModbusDebugTool/Script/ViewModbusItem.cs" id="1_ih8g3"]
[ext_resource type="FontFile" uid="uid://b1atsgy4xkk7d" path="res://Font/SourceHanSansCN-Regular.otf" id="1_upshn"]
[node name="ModbusItem" type="Control"]
custom_minimum_size = Vector2(200, 200)
layout_mode = 3
anchors_preset = 0
script = ExtResource("1_ih8g3")
[node name="BackGround" type="ColorRect" parent="."]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
color = Color(0, 0, 0, 0.196078)
[node name="ColorRect" type="ColorRect" parent="."]
custom_minimum_size = Vector2(200, 37)
layout_mode = 1
anchors_preset = 5
anchor_left = 0.5
anchor_right = 0.5
offset_left = -100.0
offset_right = 100.0
offset_bottom = 37.0
grow_horizontal = 2
color = Color(0, 0, 0, 1)
[node name="Title" type="Label" parent="."]
layout_mode = 1
anchors_preset = 10
anchor_right = 1.0
offset_bottom = 37.0
grow_horizontal = 2
theme_override_fonts/font = ExtResource("1_upshn")
theme_override_font_sizes/font_size = 25
text = "保持寄存器65535"
horizontal_alignment = 1
[node name="Value" type="Label" parent="."]
layout_mode = 1
anchors_preset = 4
anchor_top = 0.5
anchor_bottom = 0.5
offset_left = 93.0
offset_top = -50.0
offset_right = 149.0
offset_bottom = -20.0
grow_vertical = 2
theme_override_fonts/font = ExtResource("1_upshn")
theme_override_font_sizes/font_size = 20
text = "65535"
horizontal_alignment = 1
[node name="ID" type="Label" parent="."]
layout_mode = 1
anchors_preset = 4
anchor_top = 0.5
anchor_bottom = 0.5
offset_left = 9.0
offset_top = -50.0
offset_right = 88.0
offset_bottom = -20.0
grow_vertical = 2
theme_override_fonts/font = ExtResource("1_upshn")
theme_override_font_sizes/font_size = 20
text = "1-1"
horizontal_alignment = 1
[node name="WriteEdit" type="LineEdit" parent="."]
layout_mode = 1
anchors_preset = 4
anchor_top = 0.5
anchor_bottom = 0.5
offset_left = 8.0
offset_right = 120.0
offset_bottom = 38.0
grow_vertical = 2
theme_override_fonts/font = ExtResource("1_upshn")
theme_override_font_sizes/font_size = 20
placeholder_text = "输入写入值"
[node name="Get" type="Button" parent="."]
layout_mode = 1
anchors_preset = 6
anchor_left = 1.0
anchor_top = 0.5
anchor_right = 1.0
anchor_bottom = 0.5
offset_left = -46.0
offset_top = -50.0
offset_right = -6.0
offset_bottom = -19.0
grow_horizontal = 0
grow_vertical = 2
focus_mode = 0
text = "获取"
[node name="WriteBtn" type="Button" parent="."]
layout_mode = 1
anchors_preset = 6
anchor_left = 1.0
anchor_top = 0.5
anchor_right = 1.0
anchor_bottom = 0.5
offset_left = -74.0
offset_right = -6.0
offset_bottom = 38.0
grow_horizontal = 0
grow_vertical = 2
focus_mode = 0
theme_override_fonts/font = ExtResource("1_upshn")
theme_override_font_sizes/font_size = 20
text = "写入"
[node name="ModifyBtn" type="Button" parent="."]
layout_mode = 1
anchors_preset = 2
anchor_top = 1.0
anchor_bottom = 1.0
offset_left = 13.0
offset_top = -51.0
offset_right = 81.0
offset_bottom = -13.0
grow_vertical = 0
focus_mode = 0
theme_override_font_sizes/font_size = 20
text = "修改"
[node name="DeleteBtn" type="Button" parent="."]
layout_mode = 1
anchors_preset = 3
anchor_left = 1.0
anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = -81.0
offset_top = -51.0
offset_right = -13.0
offset_bottom = -13.0
grow_horizontal = 0
grow_vertical = 0
focus_mode = 0
theme_override_font_sizes/font_size = 20
text = "删除"
[connection signal="pressed" from="Get" to="." method="GetValue"]
[connection signal="pressed" from="WriteBtn" to="." method="WriteValue"]
[connection signal="pressed" from="ModifyBtn" to="." method="OnModifyItem"]
[connection signal="pressed" from="DeleteBtn" to="." method="OnDeleteItem"]

32
Example/ModbusDebugTool/Script/DataModbusItem.cs

@ -0,0 +1,32 @@ @@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
namespace EGFramework.Examples.ModbusDebugTool{
public class DataModbus{
/// <summary>
/// Key is the ModbusRTU Type + "-" + DeviceAddress + "-" + RegisterAddress + "-" + Port
/// </summary>
/// <typeparam name="string">Key is the ModbusRTU DeviceAddress + "-" + Type + "-" + RegisterAddress + "-" + Port</typeparam>
/// <typeparam name="DataModbusItem"></typeparam>
/// <returns></returns>
public Dictionary<string,DataModbusItem> Items { set; get; } = new Dictionary<string,DataModbusItem>();
}
public class DataModbusItem
{
public ModbusRegisterType Type { set; get; } = ModbusRegisterType.HoldingRegister;
public byte DeviceAddress { set; get; }
public ushort RegisterAddress { set; get; }
public string SerialPort { set; get; }
public string GetKey(){
return Type + "-" + DeviceAddress + "-" + RegisterAddress + "-" + SerialPort;
}
}
public class DataModbusSettings{
public int BaudRate{ set; get; } = 115200;
}
}

102
Example/ModbusDebugTool/Script/ViewEdit.cs

@ -0,0 +1,102 @@ @@ -0,0 +1,102 @@
using Godot;
using System;
using System.Collections.Generic;
using System.IO.Ports;
namespace EGFramework.Examples.ModbusDebugTool{
public partial class ViewEdit : Control,IEGFramework
{
public DataModbusItem DataModbusItem { set; get; }
public ModbusRegisterType ModbusRegisterType { set; get; }
public Label Title { set; get; }
public LineEdit EditDeviceAddress { set; get; }
public LineEdit EditRegisterAddress { set; get; }
public OptionButton SerialPortSelect { set; get; }
public ViewMenu ViewMenu { set; get; }
public Dictionary<string,int> SerialPortMapping { set; get; } = new Dictionary<string, int>();
public override void _Ready()
{
Title = this.GetNode<Label>("Title");
SerialPortSelect = this.GetNode<OptionButton>("List/SerialPortSelect");
EditDeviceAddress = this.GetNode<LineEdit>("List/Device");
EditRegisterAddress = this.GetNode<LineEdit>("List/Register");
ViewMenu = this.GetNode<ViewMenu>("/root/Menu");
ModbusRegisterType = ModbusRegisterType.HoldingRegister;
RefreshSerialPort();
}
public void RefreshSerialPort(){
string[] portNames = SerialPort.GetPortNames();
SerialPortMapping.Clear();
int index = 0;
SerialPortSelect.Clear();
foreach (string portName in portNames)
{
SerialPortMapping.Add(portName,index);
SerialPortSelect.AddItem(portName,index);
index++;
}
}
public void Modify(DataModbusItem modbusItem){
DataModbusItem = modbusItem;
ModbusRegisterType = modbusItem.Type;
Title.Text = ModbusRegisterType.ToString();
EditDeviceAddress.Text = modbusItem.DeviceAddress.ToString();
EditRegisterAddress.Text = modbusItem.RegisterAddress.ToString();
if(SerialPortMapping.ContainsKey(modbusItem.SerialPort)){
SerialPortSelect.Selected = SerialPortMapping[modbusItem.SerialPort];
}
}
public void New(ModbusRegisterType type){
DataModbusItem = null;
ModbusRegisterType = type;
Title.Text = ModbusRegisterType.ToString();
}
public void OnClose(){
this.Visible = false;
}
public void Save(){
try
{
DataModbusItem modbusItem = new DataModbusItem(){
DeviceAddress = (byte)int.Parse(EditDeviceAddress.Text),
RegisterAddress = (ushort)int.Parse(EditRegisterAddress.Text),
SerialPort = SerialPortSelect.GetItemText(SerialPortSelect.GetSelectedId()),
Type = ModbusRegisterType
};
if(DataModbusItem != null){
this.EGGetObject<DataModbus>().Items.Remove(DataModbusItem.GetKey());
}
this.EGGetObject<DataModbus>().Items.Add(modbusItem.GetKey(),modbusItem);
this.EGSave().SetDataToFile(this.EGGetObject<DataModbus>());
this.Visible = false;
this.ViewMenu.RefreshSaveData();
}
catch (System.Exception ex)
{
GD.Print("Save Exception" + ex);
}
}
}
public static class ViewEditExtension{
public static void OnModifyEdit(this Node self,DataModbusItem dataModbusItem){
self.GetTree().CurrentScene.GetNode<ViewEdit>("Edit").Visible = true;
self.GetTree().CurrentScene.GetNode<ViewEdit>("Edit").Modify(dataModbusItem);
}
public static void OnNewEdit(this Node self,ModbusRegisterType type){
self.GetTree().CurrentScene.GetNode<ViewEdit>("Edit").Visible = true;
self.GetTree().CurrentScene.GetNode<ViewEdit>("Edit").New(type);
}
}
}

78
Example/ModbusDebugTool/Script/ViewMenu.cs

@ -0,0 +1,78 @@ @@ -0,0 +1,78 @@
using Godot;
using System.Collections.Generic;
namespace EGFramework.Examples.ModbusDebugTool{
public partial class ViewMenu : Node,IEGFramework
{
[Export] public PackedScene StorageItem = GD.Load<PackedScene>("res://Example/ModbusDebugTool/Component/modbus_item.tscn");
public PopupMenu MenuAdd;
public PopupMenu MenuSettings;
public GridContainer ModbusItemContainer;
public ViewEdit Edit;
public EasyEvent<ModbusRTU_Response> OnModbusRTUGet { set; get; } = new EasyEvent<ModbusRTU_Response>();
public override void _Ready()
{
MenuAdd = this.GetNode<PopupMenu>("MenuBar/添加");
MenuSettings = this.GetNode<PopupMenu>("MenuBar/设置");
ModbusItemContainer = this.GetNode<GridContainer>("Scroll/ModbusList");
Edit = this.GetNode<ViewEdit>("Edit");
Edit.Visible = false;
this.EGEnabledProtocolTools();
DataModbus dataModbus = this.EGSave().GetDataByFile<DataModbus>();
if (dataModbus == null)
{
dataModbus = new DataModbus();
}
this.EGRegisterObject(dataModbus);
RefreshSaveData();
this.EGRegisterMessageEvent<ModbusRTU_Response>((e,sender,ProtocolType)=>{
if(ProtocolType == ProtocolType.SerialPort){
this.AppendMessage("【接收-"+sender+"】 "+e.SourceData.ToStringByHex());
OnModbusRTUGet.Invoke(e);
}
});
this.EGOnMessage<ModbusRTU_Response>();
}
public void OpenEdit(int AddMenuId){
GD.Print(MenuAdd.GetItemText(AddMenuId));
switch(MenuAdd.GetItemText(AddMenuId)){
case "保持寄存器":
this.OnNewEdit(ModbusRegisterType.HoldingRegister);
break;
default:
break;
}
}
public void OpenSettings(int OtherId){
switch(MenuSettings.GetItemText(OtherId)){
case "查看报文":
this.ViewMessage().Visible = true;
break;
case "设置":
this.ViewSettings().Visible = true;
break;
default:
break;
}
}
public void ReadAll(int ReadMenuId){
GD.Print(MenuAdd.GetItemText(ReadMenuId));
}
public void RefreshSaveData(){
ModbusItemContainer.ClearChildren();
if(this.EGGetObject<DataModbus>() != null){
foreach(DataModbusItem item in this.EGGetObject<DataModbus>().Items.Values){
ViewModbusItem viewItem = StorageItem.Instantiate<ViewModbusItem>();
ModbusItemContainer.AddChild(viewItem);
viewItem.LoadData(item);
}
}
}
}
}

33
Example/ModbusDebugTool/Script/ViewMessage.cs

@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
using Godot;
using System;
namespace EGFramework.Examples.ModbusDebugTool{
public partial class ViewMessage : Control
{
public TextEdit MessageContainer { set; get; }
public override void _Ready()
{
this.Visible = false;
MessageContainer = this.GetNode<TextEdit>("MessageContainer");
}
public void OnClose(){
this.Visible = false;
}
public void Clear(){
MessageContainer.Text = "";
}
public void AppendMessage(string msg){
MessageContainer.Text += msg + "\n";
}
}
public static class ViewMessageExtension{
public static ViewMessage ViewMessage(this Node self){
return self.GetTree().CurrentScene.GetNode<ViewMessage>("Message");
}
public static void AppendMessage(this Node self,string msg){
self.GetTree().CurrentScene.GetNode<ViewMessage>("Message").AppendMessage(msg);
}
}
}

99
Example/ModbusDebugTool/Script/ViewModbusItem.cs

@ -0,0 +1,99 @@ @@ -0,0 +1,99 @@
using Godot;
using System;
namespace EGFramework.Examples.ModbusDebugTool{
public partial class ViewModbusItem : Node,IEGFramework
{
public DataModbusItem DataModbusItem { set; get; }
public Label ID { set; get; }
public Label Title { set; get; }
public Label Value { set; get; }
public LineEdit InputData { set; get; }
private bool IsLoadUI { set; get; }
public override void _Ready()
{
}
public void LoadUI(){
if(!IsLoadUI){
Title = this.GetNode<Label>("Title");
Value = this.GetNode<Label>("Value");
ID = this.GetNode<Label>("ID");
InputData = this.GetNode<LineEdit>("WriteEdit");
InputData.Text = "0";
IsLoadUI = true;
}
}
public void LoadData(DataModbusItem data){
LoadUI();
this.DataModbusItem = data;
Title.Text = data.Type.ToString();
ID.Text = data.DeviceAddress+"-"+data.RegisterAddress;
Value.Text = "";
}
public void OnModifyItem(){
this.OnModifyEdit(DataModbusItem);
}
public void WriteValue(){
try
{
IRequest WriteRequest;
switch(DataModbusItem.Type){
case ModbusRegisterType.HoldingRegister:
WriteRequest = new ModbusRTU_WriteSingleHoldingRegister
(DataModbusItem.DeviceAddress,DataModbusItem.RegisterAddress,ushort.Parse(InputData.Text));
this.AppendMessage("【发送-"+DataModbusItem.SerialPort+"】 "+WriteRequest.ToProtocolByteData().ToStringByHex());
this.EGSendMessage(WriteRequest,DataModbusItem.SerialPort,ProtocolType.SerialPort);
break;
}
}
catch (System.Exception ex)
{
GD.PrintErr(ex);
}
}
public void OnDeleteItem(){
if(this.EGGetObject<DataModbus>().Items.ContainsKey(DataModbusItem.GetKey())){
this.EGGetObject<DataModbus>().Items.Remove(DataModbusItem.GetKey());
}
this.EGSave().SetDataToFile(this.EGGetObject<DataModbus>());
this.GetNode<ViewMenu>("/root/Menu").RefreshSaveData();
}
public void GetValue(){
IRequest ReadRequest;
switch(DataModbusItem.Type){
case ModbusRegisterType.HoldingRegister:
ReadRequest = new ModbusRTU_ReadHoldingRegisters
(DataModbusItem.DeviceAddress,DataModbusItem.RegisterAddress,1);
this.AppendMessage("【发送-"+DataModbusItem.SerialPort+"】 "+ReadRequest.ToProtocolByteData().ToStringByHex());
this.EGSendMessage(ReadRequest,DataModbusItem.SerialPort,ProtocolType.SerialPort);
this.EGSerialPort().SetExpectReceivedDataLength(6);
break;
}
this.GetNode<ViewMenu>("/root/Menu").OnModbusRTUGet.Register(OnValueGet);
}
public void OnValueGet(ModbusRTU_Response e){
if((int)e.FunctionType == (int)DataModbusItem.Type && DataModbusItem.DeviceAddress == e.DeviceAddress){
switch(DataModbusItem.Type){
case ModbusRegisterType.HoldingRegister:
this.Value.Text = e.HoldingRegister[0].ToString();
break;
}
}
this.GetNode<ViewMenu>("/root/Menu").OnModbusRTUGet.UnRegister(OnValueGet);
}
}
}

55
Example/ModbusDebugTool/Script/ViewSettings.cs

@ -0,0 +1,55 @@ @@ -0,0 +1,55 @@
using Godot;
using System;
namespace EGFramework.Examples.ModbusDebugTool{
public partial class ViewSettings : Control,IEGFramework
{
public LineEdit EditBaudRate { set; get; }
public override void _Ready()
{
EditBaudRate = this.GetNode<LineEdit>("List/Device");
this.Visible = false;
LoadSettings();
}
public void LoadSettings(){
DataModbusSettings settings = this.EGSave().GetDataByFile<DataModbusSettings>();
this.EGRegisterObject(settings);
UpdateSettings(settings);
EditBaudRate.Text = settings.BaudRate.ToString();
}
public void OnClose(){
this.Visible = false;
}
public void UpdateSettings(DataModbusSettings settings){
this.EGSerialPort().SetBaudRate(settings.BaudRate);
}
public void Save(){
try
{
DataModbusSettings settings = new DataModbusSettings(){
BaudRate = int.Parse(EditBaudRate.Text)
};
UpdateSettings(settings);
this.EGRegisterObject(settings);
this.EGSave().SetDataToFile(this.EGGetObject<DataModbusSettings>());
this.Visible = false;
}
catch (System.Exception ex)
{
GD.Print("Save Exception" + ex);
}
}
}
public static class ViewSettingsExtension{
public static ViewSettings ViewSettings(this Node self){
return self.GetTree().CurrentScene.GetNode<ViewSettings>("Settings");
}
}
}

417
Example/ModbusDebugTool/TestModbus.tscn

@ -0,0 +1,417 @@ @@ -0,0 +1,417 @@
[gd_scene load_steps=7 format=3 uid="uid://qo2w16ececmu"]
[ext_resource type="Script" path="res://Example/ModbusDebugTool/Script/ViewMenu.cs" id="1_7ubyu"]
[ext_resource type="FontFile" uid="uid://b1atsgy4xkk7d" path="res://Font/SourceHanSansCN-Regular.otf" id="1_tw1qu"]
[ext_resource type="PackedScene" uid="uid://c8kk7iegicmdr" path="res://Example/ModbusDebugTool/Component/modbus_item.tscn" id="3_c8jeq"]
[ext_resource type="Script" path="res://Example/ModbusDebugTool/Script/ViewEdit.cs" id="4_smsvf"]
[ext_resource type="Script" path="res://Example/ModbusDebugTool/Script/ViewMessage.cs" id="5_8s6lk"]
[ext_resource type="Script" path="res://Example/ModbusDebugTool/Script/ViewSettings.cs" id="6_38tri"]
[node name="Menu" type="Control"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_7ubyu")
[node name="ColorRect" type="ColorRect" parent="."]
visible = false
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
color = Color(0, 0, 0, 1)
[node name="Title" type="Label" parent="."]
layout_mode = 1
anchors_preset = 5
anchor_left = 0.5
anchor_right = 0.5
offset_left = -253.0
offset_top = 52.0
offset_right = 251.0
offset_bottom = 128.0
grow_horizontal = 2
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 10
theme_override_fonts/font = ExtResource("1_tw1qu")
theme_override_font_sizes/font_size = 52
text = "Modbus调试工具V1.0"
[node name="Scroll" type="ScrollContainer" parent="."]
layout_mode = 1
anchors_preset = -1
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = 10.0
offset_top = 140.0
offset_right = -9.0
grow_horizontal = 2
grow_vertical = 0
[node name="ModbusList" type="GridContainer" parent="Scroll"]
layout_mode = 2
theme_override_constants/h_separation = 30
theme_override_constants/v_separation = 30
columns = 5
[node name="ModbusItem" parent="Scroll/ModbusList" instance=ExtResource("3_c8jeq")]
layout_mode = 2
[node name="MenuBar" type="MenuBar" parent="."]
layout_mode = 1
anchors_preset = 10
anchor_right = 1.0
offset_bottom = 45.0
grow_horizontal = 2
theme_override_fonts/font = ExtResource("1_tw1qu")
theme_override_font_sizes/font_size = 25
[node name="添加" type="PopupMenu" parent="MenuBar"]
size = Vector2i(108, 116)
item_count = 4
item_0/text = "线圈"
item_0/id = 0
item_1/text = "离散输入"
item_1/id = 1
item_2/text = "保持寄存器"
item_2/id = 2
item_3/text = "输入寄存器"
item_3/id = 3
[node name="读取" type="PopupMenu" parent="MenuBar"]
size = Vector2i(108, 116)
item_count = 4
item_0/text = "线圈"
item_0/id = 0
item_1/text = "离散输入"
item_1/id = 1
item_2/text = "保持寄存器"
item_2/id = 2
item_3/text = "输入寄存器"
item_3/id = 3
[node name="帮助" type="PopupMenu" parent="MenuBar"]
item_count = 3
item_0/text = "关于jkpete"
item_0/id = 0
item_1/text = "关于EGFramework"
item_1/id = 1
item_2/text = "使用手册"
item_2/id = 2
[node name="设置" type="PopupMenu" parent="MenuBar"]
item_count = 2
item_0/text = "查看报文"
item_0/id = 0
item_1/text = "设置"
item_1/id = 1
[node name="Message" type="Control" parent="."]
layout_mode = 1
anchor_top = 0.558642
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("5_8s6lk")
metadata/_edit_use_anchors_ = true
[node name="MessageContainer" type="TextEdit" parent="Message"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
editable = false
[node name="Close" type="Button" parent="Message"]
layout_mode = 1
anchors_preset = 1
anchor_left = 1.0
anchor_right = 1.0
offset_left = -40.0
offset_bottom = 31.0
grow_horizontal = 0
text = "关闭"
[node name="Clear" type="Button" parent="Message"]
layout_mode = 1
anchors_preset = 1
anchor_left = 1.0
anchor_right = 1.0
offset_left = -82.0
offset_right = -42.0
offset_bottom = 31.0
grow_horizontal = 0
text = "清空"
[node name="Edit" type="Control" parent="."]
visible = false
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("4_smsvf")
[node name="Back" type="ColorRect" parent="Edit"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
color = Color(0, 0, 0, 0.392157)
[node name="Back2" type="ColorRect" parent="Edit"]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -300.0
offset_top = -300.0
offset_right = 300.0
offset_bottom = 300.0
grow_horizontal = 2
grow_vertical = 2
color = Color(0.0588235, 0.231373, 0.270588, 1)
[node name="Close" type="Button" parent="Edit/Back2"]
layout_mode = 1
anchors_preset = 1
anchor_left = 1.0
anchor_right = 1.0
offset_left = -50.0
offset_bottom = 50.0
grow_horizontal = 0
text = "X"
[node name="Title" type="Label" parent="Edit"]
layout_mode = 1
anchors_preset = 5
anchor_left = 0.5
anchor_right = 0.5
offset_left = -168.0
offset_top = 54.0
offset_right = 168.0
offset_bottom = 124.0
grow_horizontal = 2
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 10
theme_override_fonts/font = ExtResource("1_tw1qu")
theme_override_font_sizes/font_size = 48
text = "保持寄存器"
horizontal_alignment = 1
[node name="List" type="VBoxContainer" parent="Edit"]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -264.0
offset_top = -174.0
offset_right = 265.0
offset_bottom = 117.0
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/separation = 24
alignment = 1
[node name="Device" type="LineEdit" parent="Edit/List"]
custom_minimum_size = Vector2(529, 64)
layout_mode = 2
theme_override_font_sizes/font_size = 24
placeholder_text = "请输入设备地址"
virtual_keyboard_type = 2
[node name="Register" type="LineEdit" parent="Edit/List"]
custom_minimum_size = Vector2(529, 64)
layout_mode = 2
theme_override_font_sizes/font_size = 24
placeholder_text = "请输入寄存器地址"
virtual_keyboard_type = 2
[node name="SerialPortSelect" type="OptionButton" parent="Edit/List"]
custom_minimum_size = Vector2(0, 64)
layout_mode = 2
theme_override_font_sizes/font_size = 24
item_count = 1
popup/item_0/text = "COM1"
popup/item_0/id = 0
[node name="Save" type="Button" parent="Edit"]
layout_mode = 1
anchors_preset = 7
anchor_left = 0.5
anchor_top = 1.0
anchor_right = 0.5
anchor_bottom = 1.0
offset_left = -80.0
offset_top = -109.0
offset_right = 80.0
offset_bottom = -45.0
grow_horizontal = 2
grow_vertical = 0
theme_override_font_sizes/font_size = 36
text = "保存"
[node name="RefreshPort" type="Button" parent="Edit"]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = 211.0
offset_top = 109.0
offset_right = 264.0
offset_bottom = 141.0
grow_horizontal = 2
grow_vertical = 2
text = "刷新"
[node name="Settings" type="Control" parent="."]
visible = false
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("6_38tri")
[node name="Back" type="ColorRect" parent="Settings"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
color = Color(0, 0, 0, 0.392157)
[node name="Back2" type="ColorRect" parent="Settings"]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -300.0
offset_top = -300.0
offset_right = 300.0
offset_bottom = 300.0
grow_horizontal = 2
grow_vertical = 2
color = Color(0.0588235, 0.231373, 0.270588, 1)
[node name="Close" type="Button" parent="Settings/Back2"]
layout_mode = 1
anchors_preset = 1
anchor_left = 1.0
anchor_right = 1.0
offset_left = -50.0
offset_bottom = 50.0
grow_horizontal = 0
text = "X"
[node name="Title" type="Label" parent="Settings"]
layout_mode = 1
anchors_preset = 5
anchor_left = 0.5
anchor_right = 0.5
offset_left = -168.0
offset_top = 54.0
offset_right = 168.0
offset_bottom = 124.0
grow_horizontal = 2
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 10
theme_override_fonts/font = ExtResource("1_tw1qu")
theme_override_font_sizes/font_size = 48
text = "设置"
horizontal_alignment = 1
[node name="List" type="VBoxContainer" parent="Settings"]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -84.0
offset_top = -174.0
offset_right = 266.0
offset_bottom = 117.0
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/separation = 24
alignment = 1
[node name="Device" type="LineEdit" parent="Settings/List"]
custom_minimum_size = Vector2(250, 64)
layout_mode = 2
theme_override_font_sizes/font_size = 24
text = "115200"
placeholder_text = "请输入波特率"
virtual_keyboard_type = 2
[node name="OptionText" type="VBoxContainer" parent="Settings"]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -214.0
offset_top = -174.0
offset_right = -114.0
offset_bottom = 117.0
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/separation = 24
alignment = 1
[node name="Label" type="Label" parent="Settings/OptionText"]
custom_minimum_size = Vector2(100, 64)
layout_mode = 2
theme_override_font_sizes/font_size = 24
text = "波特率"
horizontal_alignment = 2
vertical_alignment = 1
[node name="Save" type="Button" parent="Settings"]
layout_mode = 1
anchors_preset = 7
anchor_left = 0.5
anchor_top = 1.0
anchor_right = 0.5
anchor_bottom = 1.0
offset_left = -80.0
offset_top = -109.0
offset_right = 80.0
offset_bottom = -45.0
grow_horizontal = 2
grow_vertical = 0
theme_override_font_sizes/font_size = 36
text = "保存"
[connection signal="index_pressed" from="MenuBar/添加" to="." method="OpenEdit"]
[connection signal="index_pressed" from="MenuBar/设置" to="." method="OpenSettings"]
[connection signal="pressed" from="Message/Close" to="Message" method="OnClose"]
[connection signal="pressed" from="Message/Clear" to="Message" method="Clear"]
[connection signal="pressed" from="Edit/Back2/Close" to="Edit" method="OnClose"]
[connection signal="pressed" from="Edit/Save" to="Edit" method="Save"]
[connection signal="pressed" from="Edit/RefreshPort" to="Edit" method="RefreshSerialPort"]
[connection signal="pressed" from="Settings/Back2/Close" to="Settings" method="OnClose"]
[connection signal="pressed" from="Settings/Save" to="Settings" method="Save"]

3
Example/Theme/White/White.tres

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
[gd_resource type="Theme" format=3 uid="uid://cbf2s7b2p1p2j"]
[resource]

27
Example/UsingTest/Scene/TestEGFramework.tscn

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
[gd_scene load_steps=2 format=3 uid="uid://dfa86yq8m28rk"]
[ext_resource type="Script" path="res://Example/UsingTest/Script/EGTest.cs" id="1_s8hm3"]
[node name="TestEgFramework" type="Control"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_s8hm3")
[node name="Timer" type="Timer" parent="."]
autostart = true
[node name="Label" type="Label" parent="."]
layout_mode = 0
offset_right = 361.0
offset_bottom = 164.0
[node name="Button1" type="Button" parent="."]
layout_mode = 0
offset_right = 50.0
offset_bottom = 50.0
[connection signal="pressed" from="Button1" to="." method="OnButton1Click"]

45
Example/UsingTest/Script/EGMDnsTest.cs

@ -0,0 +1,45 @@ @@ -0,0 +1,45 @@
using Godot;
using System;
using EGFramework;
using WebDav;
using System.Threading.Tasks;
using System.Net;
public partial class EGMDnsTest : Node, IEGFramework
{
private string serverUrl = "http://192.168.1.170:5212/dav";
private string username = "ZK@zk.com";
private string password = "uQTl7lzLSMZQ1QBd7sZZMlG2Gya65XKM";
public override void _Ready()
{
//TestDav();
}
public async void TestDav(){
this.EGWebDav().InitClient(serverUrl,username,password);
await InitClient();
}
public async Task<bool> InitClient()
{
string result = EncodeCredentials(username,password);
GD.Print(result);
//ScanWebDavAndDownload();
//await this.EGWebDav().DownloadFile("/dav/Picture/风景1.jpg",ProjectSettings.GlobalizePath("user://"),"风景1.jpg");
//Print("download success!");
//await this.EGWebDav().UploadFile(ProjectSettings.GlobalizePath("user://4.mp4"),"/dav/Picture","4.mp4");
await this.EGWebDav().UploadFile(ProjectSettings.GlobalizePath("user://PPT_Test.jpg"),"/dav/Picture","PPT_Test.jpg");
GD.Print("upload success!");
//var result = await this.EGWebDav().GetList("/Video");
//Print(JsonConvert.SerializeObject(result));
return true;
}
public static string EncodeCredentials(string username, string password)
{
string credentials = $"{username}:{password}";
byte[] credentialsBytes = System.Text.Encoding.UTF8.GetBytes(credentials);
string encodedCredentials = Convert.ToBase64String(credentialsBytes);
return encodedCredentials;
}
}

313
Example/UsingTest/Script/EGTest.cs

@ -0,0 +1,313 @@ @@ -0,0 +1,313 @@
using Godot;
using static Godot.GD;
using System.Collections.Generic;
using EGFramework;
using Newtonsoft.Json;
using System;
using System.Net;
public partial class EGTest : Node,IEGFramework
{
public Label label { set; get; }
public override void _Ready()
{
this.EGEnabledProtocolTools();
this.EGMqtt().ConnectMQTTServer("192.168.1.220");
//this.EGUDP().UDPDevices[5555].Connect(IPAddress.Parse("224.0.0.251"),5353);
//byte[] sendData = { 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x5F, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x07, 0x5F, 0x64, 0x6E, 0x73, 0x2D, 0x73, 0x64, 0x04, 0x5F, 0x75, 0x64, 0x70, 0x05, 0x6C, 0x6F, 0x63, 0x61, 0x6C, 0x00, 0x00, 0x0C, 0x00, 0x01 };
//this.EGUDP().UDPDevices[5555].Send(sendData);
// this.EGRegisterMessageEvent<PrintResponse>((e,sender,protocol)=>{
// Print(sender);
// });
// this.EGOnMessage<PrintResponse>();
// this.EGReadFromFile("SaveData/MySeg2.seg2");
//TestTCPClient();
//TestSerialPort();
//TestTCPServer();
//this.EGUDP();
//this.EGUDPListen(11000);
//this.EGSendMessage(new MessageStruct(1,"xxx"),"192.168.1.192:9002",ProtocolType.UDP);
//this.EGHttpClient();
//this.EGHttpServer().Listen("http://+:6555/index/");
}
public override void _Process(double delta)
{
//Print(this.EGMqtt().MqttDevices["192.168.1.220"].IsConnected);
}
public override void _ExitTree()
{
}
public void OnButton1Click(){
this.EGMqtt().SubScribeTheme("192.168.1.220","test");
byte[] testData = {0x3A,0x55};
this.EGMqtt().PublishTheme("192.168.1.220","test",testData);
}
public void RefreshMsg(bool coil){
label.Text += coil+" ";
}
public void OnTimer(){
//this.EGSendMessage(new MessageStruct(1,"山东博裕1"),"COM4",ProtocolType.SerialPort);
//this.EGSendMessage(new MessageStruct(1,"山东博裕1"),"192.168.1.244:6060",ProtocolType.TCPClient);
//this.EGSendMessage(new ModbusTCP_WriteMultiCoil(1,0,sendData),"192.168.1.200:3000",ProtocolType.TCPClient);
//TestSingleCoil();
//TestMultiCoil();
//TestSingleHoldingRegister();
//TestMultiHoldingRegister();
}
public void TestModbus(){
label = this.GetNode<Label>("Label");
label.Text = "[Modbus]";
this.EGRegisterMessageEvent<MessageResponse>((e,sender,type)=>{
Print("[Got Response]"+"[sender = "+sender+"]"+"["+type+"]"+ e.MessageId+"||"+e.Author);
});
this.EGOnMessage<MessageResponse>();
this.EGRegisterMessageEvent<ModbusTCP_Response>((e,sender,type)=>{
if(type == ProtocolType.TCPClient && e.FunctionType == ModbusFunctionType.ReadCoil){
int registerId = 0;
foreach(bool coil in e.Coil){
Print(registerId + "Status:" + coil);
registerId++;
}
}
if(type == ProtocolType.TCPClient && e.FunctionType == ModbusFunctionType.ReadDiscreteInput){
int registerId = 0;
foreach(bool discreteInput in e.DiscreteInput){
Print(registerId + "Status:" + discreteInput);
registerId++;
}
}
if(type == ProtocolType.TCPClient && e.FunctionType == ModbusFunctionType.ReadHoldingRegisters){
int registerId = 0;
foreach(ushort holdingRegister in e.HoldingRegister){
Print(registerId + "Status:" + holdingRegister);
registerId++;
}
}
if(type == ProtocolType.TCPClient && e.FunctionType == ModbusFunctionType.ReadInputRegisters){
int registerId = 0;
foreach(ushort inputRegister in e.InputRegister){
Print(registerId + "Status:" + inputRegister);
label.Text+=registerId + "Status:" + inputRegister;
registerId++;
}
}
//this.EGOffMessage<ModbusTCP_Response>();
});
this.EGRegisterMessageEvent<ModbusRTU_Response>((e,sender,type)=>{
if(type == ProtocolType.SerialPort && e.FunctionType == ModbusFunctionType.ReadCoil){
int registerId = 0;
foreach(bool coil in e.Coil){
Print(registerId + "Status:" + coil);
registerId++;
}
}
if(type == ProtocolType.SerialPort && e.FunctionType == ModbusFunctionType.ReadDiscreteInput){
int registerId = 0;
foreach(bool discreteInput in e.DiscreteInput){
Print(registerId + "Status:" + discreteInput);
registerId++;
}
}
if(type == ProtocolType.SerialPort && e.FunctionType == ModbusFunctionType.ReadHoldingRegisters){
int registerId = 0;
foreach(ushort holdingRegister in e.HoldingRegister){
Print(registerId + "Status:" + holdingRegister);
registerId++;
}
}
if(type == ProtocolType.SerialPort && e.FunctionType == ModbusFunctionType.ReadInputRegisters){
int registerId = 0;
foreach(ushort inputRegister in e.InputRegister){
Print(registerId + "Status:" + inputRegister);
label.Text+=registerId + "Status:" + inputRegister;
registerId++;
}
}
//this.EGOffMessage<ModbusRTU_Response>();
});
}
public void TestTCPServer(){
this.EGTCPServer().OnClientConnect.Register(e=>{
Print(e +" is connected");
this.EGSendMessage(new MessageStruct(1,"Hello"),e,ProtocolType.TCPServer);
});
this.EGTCPServer().OnClientDisconnect.Register(e=>{
Print(e +" is disconnect");
});
this.EGTCPServerListen(9999);
}
public void TestSqlite(){
this.EGSqlite().SaveData(new TestBoxMessage());
List<TestBoxMessage> result = this.EGSqlite().GetDataSet<TestBoxMessage>();
if(result == null){
PrintErr(this.EGSqlite().ExceptionMsg);
}
Print("Result = " + result[0].TestDouble + result[0].TestFloat);
}
public void TestSerialPort(){
this.EGSerialPort().SetBaudRate(9600);
this.EGSendMessage(new ModbusRTU_ReadInputRegisters(1,0,2),"COM4",ProtocolType.SerialPort);
this.EGSendMessage(new ModbusRTU_ReadCoils(1,0,8),"COM4",ProtocolType.SerialPort);
this.EGOnMessage<ModbusRTU_Response>();
}
public void TestTCPClient(){
this.EGTCPClient();
this.EGSendMessage(new ModbusTCP_ReadDiscreteInput(1,0,8),"192.168.1.196:6000",ProtocolType.TCPClient);
this.EGSendMessage(new ModbusTCP_ReadInputRegisters(1,0,2),"192.168.1.196:6000",ProtocolType.TCPClient);
this.EGOnMessage<ModbusTCP_Response>();
}
private bool IsOpen = false;
public void TestSingleCoil(){
if(IsOpen){
//this.EGSendMessage(new ModbusRTU_WriteSingleCoil(1,0,false),"COM4",ProtocolType.SerialPort);
this.EGSendMessage(new ModbusTCP_WriteSingleCoil(1,0,false),"192.168.1.196:6000",ProtocolType.TCPClient);
IsOpen = false;
}else{
//this.EGSendMessage(new ModbusRTU_WriteSingleCoil(1,0,true),"COM4",ProtocolType.SerialPort);
this.EGSendMessage(new ModbusTCP_WriteSingleCoil(1,0,true),"192.168.1.196:6000",ProtocolType.TCPClient);
IsOpen = true;
}
}
public void TestMultiCoil(){
byte[] OpenCode = {0xFF};
byte[] CloseCode = {0x00};
if(IsOpen){
this.EGSendMessage(new ModbusRTU_WriteMultiCoil(1,0,CloseCode.ToBoolArray()),"COM4",ProtocolType.SerialPort);
IsOpen = false;
}else{
this.EGSendMessage(new ModbusRTU_WriteMultiCoil(1,0,OpenCode.ToBoolArray()),"COM4",ProtocolType.SerialPort);
IsOpen = true;
}
}
public void TestSingleHoldingRegister(){
if(IsOpen){
this.EGSendMessage(new ModbusRTU_WriteSingleHoldingRegister(1,0,0x00),"COM4",ProtocolType.SerialPort);
IsOpen = false;
}else{
this.EGSendMessage(new ModbusRTU_WriteSingleHoldingRegister(1,0,0x01),"COM4",ProtocolType.SerialPort);
IsOpen = true;
}
}
public void TestMultiHoldingRegister(){
ushort[] OpenCode = {1,1,1,1,1,1,1,1};
ushort[] CloseCode = {0,0,0,0,0,0,0,0};
if(IsOpen){
this.EGSendMessage(new ModbusRTU_WriteMultiHoldingRegister(1,0,CloseCode),"COM4",ProtocolType.SerialPort);
IsOpen = false;
}else{
this.EGSendMessage(new ModbusRTU_WriteMultiHoldingRegister(1,0,OpenCode),"COM4",ProtocolType.SerialPort);
IsOpen = true;
}
}
}
public class TestBoxMessage {
public int Code = 0;
public float TestFloat = 0.1f;
public double TestDouble;
public bool IsHaveMessage;
public string MessageInfo;
public MessageType MsgType;
public MessageStruct MsgStruct;
public MessagePair MsgPair;
public TestBoxMessage(){
this.Code = 1;
this.TestFloat = 1.2f;
this.TestDouble = 2.52;
this.IsHaveMessage = true;
this.MessageInfo = "DefaultInfo";
MsgType = MessageType.TypeInt;
MsgPair = new MessagePair();
MsgStruct = new MessageStruct(5,"Ad");
}
public string ToProtocolData()
{
return JsonConvert.SerializeObject(this);
}
}
public class MessagePair{
public int MessageId;
public string Author;
public string MessageInfo;
public int TimeStamp;
public MessagePair(){
this.MessageId = 10001;
this.Author = "Admin";
this.MessageInfo = "Hello every one!";
this.TimeStamp = 1690188342;
}
}
public struct MessageStruct : IRequest,IEGFramework{
public string FunctionCode;
public int MessageId;
public string Author;
public MessageStruct(int messageId,string author){
FunctionCode = "Message";
MessageId = messageId;
Author = author;
}
public byte[] ToProtocolByteData()
{
return JsonConvert.SerializeObject(this).ToBytesByEncoding("GBK");
}
public string ToProtocolData()
{
return "";
//return JsonConvert.SerializeObject(this);
}
}
public struct MessageResponse : IResponse
{
public string FunctionCode { set; get; }
public int MessageId;
public string Author;
public bool TrySetData(string protocolData, byte[] protocolBytes)
{
try
{
//GD.Print(protocolData);
MessageResponse res = JsonConvert.DeserializeObject<MessageResponse>(protocolData);
if(res.FunctionCode == "Message"){
this.FunctionCode = res.FunctionCode;
this.MessageId = res.MessageId;
this.Author = res.Author;
return true;
}
return false;
}
catch (Exception)
{
return false;
}
}
}
public struct PrintResponse : IResponse
{
public bool TrySetData(string protocolData, byte[] protocolBytes)
{
//Print("Received String is " + protocolData);
Print("Received bytes is " + protocolBytes.ToStringByHex());
return true;
}
}
public enum MessageType{
TypeString = 1,
TypeInt = 2,
TypeObject = 3,
TypeArray = 4
}

BIN
Font/SourceHanSansCN-Regular.otf

Binary file not shown.

33
Font/SourceHanSansCN-Regular.otf.import

@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
[remap]
importer="font_data_dynamic"
type="FontFile"
uid="uid://b1atsgy4xkk7d"
path="res://.godot/imported/SourceHanSansCN-Regular.otf-a74baadd242fcbfb420558b64440f859.fontdata"
[deps]
source_file="res://Font/SourceHanSansCN-Regular.otf"
dest_files=["res://.godot/imported/SourceHanSansCN-Regular.otf-a74baadd242fcbfb420558b64440f859.fontdata"]
[params]
Rendering=null
antialiasing=1
generate_mipmaps=false
multichannel_signed_distance_field=false
msdf_pixel_range=8
msdf_size=48
allow_system_fallback=true
force_autohinter=false
hinting=1
subpixel_positioning=1
oversampling=0.0
Fallbacks=null
fallbacks=[]
Compress=null
compress=true
preload=[]
language_support={}
script_support={}
opentype_features={}

9
LICENSE

@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
MIT License
Copyright (c) 2024 petejk
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

210
ReadMe.md

@ -0,0 +1,210 @@ @@ -0,0 +1,210 @@
# EGFramework 使用手册
---
# 引言
> `EGFramework`全称Everyone's Godot framework,基于`Godot`开源引擎,使用C#编写,目前仅兼容`Godot4.2 - .NET`,部分兼容`Godot3.5 - .NET`,是完全开源的组件式,功能分布的框架,使用时可以自己根据需求安装对应的`Module`来实现对应的功能,同样也可以裁剪对应的`Module`来删减对应的功能。
>
> `Module` 是`EGFramework`的核心组成部分,也是`EGFramework`中重要的扩展依据。详细可以查看第二部分-Module。
# 关于Godot引擎(摘自[Godot Engine (4.x) 简体中文文档](https://docs.godotengine.org/zh-cn/4.x/about/introduction.html))
> Godot 引擎是一款功能丰富的跨平台游戏引擎,可以通过统一的界面创建 2D 和 3D
> 游戏。它提供了一套全面的通用工具,因此用户可以专注于制作游戏,而无需重新发明轮子。游戏可以一键导出到多个平台,包括主流的桌面平台(Linux、macOS、Windows)、移动平台(Android、iOS)、基于
> Web 的平台以及主机平台。
>
> Godot 在 [宽松的 MIT 许可证](https://docs.godotengine.org/zh-cn/4.x/about/complying_with_licenses.html#doc-complying-with-licenses) 下完全自由且开源,没有附加条件、没有抽成、什么都没有。用户的游戏乃至引擎的每一行代码,都归用户自己所有。Godot 的开发完全独立且由社区驱动,允许用户为满足需求重塑引擎。它受到不以盈利为目标的 [Godot 基金会](https://godot.foundation/)支持。
# 一、准备工作
## 1.下载开发环境
本章节所有环境均在开源IDE `VSCode`,`Godot4.2 - .NET` 的环境下进行,请确保已经下载好`VSCode`,`Godot4.2 - .NET`,如果您有购买Rider或者其他编码工具可以根据自己的喜好来替代VSCode。
## 2.VSCode 插件安装
打开VSCode, 按下`Ctrl+Shift+X`,在搜索栏里面搜索`C# Tools for Godot`,安装该插件,因为该插件可能依赖于`C#`插件,同时需要安装`C#`插件。
同上,搜索并安装`NuGet Package Manager GUI`插件,用于Nuget包的安装与管理。
## 3.Nuget包的安装
打开VSCode,按下`Ctrl+Shift+P`,在搜索栏里面搜索`Nuget Package ManagerGUI`,选择右侧`Install New Pakage` ,依次安装以下依赖包:
- System.IO.Ports(仅EGSerialPort 使用)
- Newtonsoft.Json(使用场合较多)
- Microsoft.Data.Sqlite(仅EGSQLite使用)
- System.Text.Encoding.CodePages(ProtocolTools目录下均有使用)
注意:这些Nuget包仅为目前框架版本所用,后续可能会有新的Nuget包导入,会在此处列出。
## 4.删除不需要的Module(可选)
如果您只想使用部分功能,或者不想安装对应的Nuget包依赖,可以直接删除对应的Module脚本文件。
无需担心,除了部分Module存在相关依赖,大部分Module是支持直接删除的。
# 二、框架简介
由于该框架使用了较多的Nuget包实现相关的功能,所以安装较为繁琐,同时因为用到较多的扩展方法,提示词可能比较冗余。您可以通过删除对应的Module来定制化自己的框架,也可以新增自己的Module来扩展自己的框架。
本框架所使用的一切Nuget包均满足MIT开源协议,如果使用的Nuget包牵扯有其他协议请联系作者QQ:1031139173,会删除对应的Module功能保证该框架满足MIT协议。
协议文件均存放在目录addons\EGFramework\License_Third_Part下面
## 1.使用框架
添加using,并继承接口IEGFramework,即可使用该框架了。
以下以一个EGSave的例子,来展示一下框架的存档功能,其他详细用法可以参阅查看Manual-EGSave对应的部分。
```csharp
using Godot;
using static Godot.GD;
using System.Collections.Generic;
using EGFramework;
public partial class EGTest : Node,IEGFramework{
DataTest dataTest = this.EGSave().GetDataByFile<DataTest>();
if (dataTest == null)
{
dataTest = new DataTest();
dataTest.PlayerName = "Player1";
dataTest.Hp = 100;
        this.EGSave().SetDataToFile(dataTest);
}
}
public class DataTest{
public string PlayerName;
public int Hp;
}
```
## 2.直接使用Module
继承接口IEGFramework时,可以直接通过this.GetModule这个扩展方法直接获取该模块。上面的代码可以通过直接调用的方式改写成如下:
```csharp
using Godot;
using static Godot.GD;
using System.Collections.Generic;
using EGFramework;
public partial class EGTest : Node,IEGFramework{
DataTest dataTest = this.GetModule<EGSave>().GetDataByFile<DataTest>();
if (dataTest == null)
{
dataTest = new DataTest();
dataTest.PlayerName = "Player1";
dataTest.Hp = 100;
this.GetModule<EGSave>().SetDataToFile(dataTest);
}
}
public class DataTest{
public string PlayerName;
public int Hp;
}
```
## 3.扩展框架(编写Module)
编写框架时,要用到IModule这个接口,任何继承了该接口的类均视为Module,可以被上面的方法Get到。
同时我们提供了一个简单的实现接口的抽象类EGModule,继承该类等同于实现接口IModule。
注意的是,因为C#仅支持单继承,如果您需要继承其他工具类或者是Godot的对应类,您应该使用接口而不是抽象类,具体用法可以参考ProtocolTools.EGProtocolSchedule这个类,它通过一个Node的生命周期的_Process()方法实现了特定线程中的消息拾取到主线程的功能。
一个简单的单例对象扩展可以写成如下:
```csharp
using System;
using System.Collections.Generic;
namespace EGFramework
{
public interface IEGObject
{
void RegisterObject<T>(T object_);
T GetObject<T>() where T : class,new();
}
public class EGObject : EGModule,IEGObject
{
private IOCContainer ObjectContainer = new IOCContainer();
public override void Init()
{
}
public TObject GetObject<TObject>() where TObject : class,new()
{
if (!ObjectContainer.self.ContainsKey(typeof(TObject)))
{
this.RegisterObject(new TObject());
}
return ObjectContainer.Get<TObject>();
}
public void RegisterObject<TObject>(TObject object_)
{
ObjectContainer.Register(object_);
}
public bool ContainsObject<TObject>(){
return ObjectContainer.self.ContainsKey(typeof(TObject));
}
}
public static class CanGetObjectExtension
{
public static T EGGetObject<T>(this IEGFramework self) where T : class,new()
{
return EGArchitectureImplement.Interface.GetModule<EGObject>().GetObject<T>();
}
}
public static class CanRegisterObjectExtension
{
public static void EGRegisterObject<T>(this IArchitecture self,T object_) where T : class,new()
{
self.GetModule<EGObject>().RegisterObject(object_);
}
public static void EGRegisterObject<T>(this IEGFramework self,T object_) where T : class,new()
{
EGArchitectureImplement.Interface.GetModule<EGObject>().RegisterObject(object_);
}
}
public static class CanContainsObjectExtension{
public static bool EGContainsObject<T>(this IEGFramework self)
{
return EGArchitectureImplement.Interface.GetModule<EGObject>().ContainsObject<T>();
}
}
}
```

211
addons/EGFramework/EGFramework.cs

@ -0,0 +1,211 @@ @@ -0,0 +1,211 @@
using System;
using System.Collections.Generic;
namespace EGFramework
{
#region Architecture & Module
public class EGArchitecture<T> : IArchitecture where T : EGArchitecture<T>, new()
{
private static T Architecture;
public static IArchitecture Interface
{
get
{
if (Architecture == null)
{
MakeSureArchitecture();
}
return Architecture;
}
}
private static void MakeSureArchitecture()
{
if (Architecture == null)
{
Architecture = new T();
Architecture.Init();
}
}
protected virtual void Init()
{
}
private IOCContainer ModuleContainer = new IOCContainer();
public void RegisterModule<TModule>(TModule module) where TModule : IModule
{
ModuleContainer.Register<TModule>(module);
module.Init();
}
public TModule GetModule<TModule>() where TModule : class, IModule,new()
{
if (!ModuleContainer.self.ContainsKey(typeof(TModule)))
{
this.RegisterModule(new TModule());
}
return ModuleContainer.Get<TModule>();
}
public bool IsInitModule<TModule>() where TModule : class, IModule,new()
{
if (!ModuleContainer.self.ContainsKey(typeof(TModule)))
{
return true;
}else{
return false;
}
}
}
public abstract class EGModule:IModule{
IArchitecture IBelongToArchitecture.GetArchitecture()
{
return EGArchitectureImplement.Interface;
}
void IModule.Init()
{
this.Init();
}
public abstract void Init();
}
#endregion
#region Interface
public interface IArchitecture
{
void RegisterModule<T>(T model) where T : IModule;
T GetModule<T>() where T : class, IModule,new();
bool IsInitModule<T>() where T : class, IModule,new();
}
public interface IModule : IBelongToArchitecture
{
void Init();
}
public interface IBelongToArchitecture
{
IArchitecture GetArchitecture();
}
#endregion
#region IOC
public class IOCContainer
{
private Dictionary<Type, object> Instances = new Dictionary<Type, object>();
public void Register<T>(T instance)
{
var key = typeof(T);
if (Instances.ContainsKey(key))
{
Instances[key] = instance;
}
else
{
Instances.Add(key, instance);
}
}
public T Get<T>() where T : class
{
var key = typeof(T);
if (Instances.TryGetValue(key, out var retInstance))
{
return retInstance as T;
}
return null;
}
public Dictionary<Type, object> self => Instances;
}
#endregion
#region Event
public interface IEasyEvent {
}
public interface IUnRegister
{
void UnRegister();
}
public class EasyEvent<T> : IEasyEvent
{
private Action<T> OnEvent = e => { };
public IUnRegister Register(Action<T> onEvent)
{
OnEvent += onEvent;
return new CustomUnRegister(() => { UnRegister(onEvent); });
}
public void UnRegister(Action<T> onEvent)
{
OnEvent -= onEvent;
}
public void Invoke(T t)
{
OnEvent?.Invoke(t);
}
}
public class EasyEvent : IEasyEvent
{
private Action OnEvent = () => { };
public IUnRegister Register(Action onEvent)
{
OnEvent += onEvent;
return new CustomUnRegister(() => { UnRegister(onEvent); });
}
public void UnRegister(Action onEvent)
{
OnEvent -= onEvent;
}
public void Invoke()
{
OnEvent?.Invoke();
}
}
public struct CustomUnRegister : IUnRegister
{
/// <summary>
/// 委托对象
/// </summary>
private Action OnUnRegister { get; set; }
/// <summary>
/// 带参构造函数
/// </summary>
/// <param name="onDispose"></param>
public CustomUnRegister(Action onUnRegister)
{
OnUnRegister = onUnRegister;
}
/// <summary>
/// 资源释放
/// </summary>
public void UnRegister()
{
OnUnRegister.Invoke();
OnUnRegister = null;
}
}
#endregion
#region FrameworkExtension
public interface IEGFramework{}
public class EGArchitectureImplement:EGArchitecture<EGArchitectureImplement>{
protected override void Init()
{
//base.Init();
}
}
public static class EGArchitectureImplementExtension{
public static T GetModule<T>(this IEGFramework self) where T : class, IModule,new()
{
return EGArchitectureImplement.Interface.GetModule<T>();
}
public static void RegisterModule<T>(this IEGFramework self,T model) where T : class, IModule,new()
{
EGArchitectureImplement.Interface.RegisterModule(model);
}
}
#endregion
}

21
addons/EGFramework/License_Third_Part/BACnet/MIT_license.txt

@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 Yabe project
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

22
addons/EGFramework/License_Third_Part/MQTTnet/LICENSE

@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) .NET Foundation and Contributors
All Rights Reserved
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

23
addons/EGFramework/License_Third_Part/Microsoft_Data_SQLite/LICENSE.txt

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
The MIT License (MIT)
Copyright (c) .NET Foundation and Contributors
All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

20
addons/EGFramework/License_Third_Part/NewtonSoft_Json/LICENSE.md

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2007 James Newton-King
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

21
addons/EGFramework/License_Third_Part/WebDavClient/LICENSE.txt

@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Sergey Kazantsev
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

96
addons/EGFramework/License_Third_Part/source_han_sans/LICENSE.txt

@ -0,0 +1,96 @@ @@ -0,0 +1,96 @@
Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font
Name 'Source'. Source is a trademark of Adobe in the United States
and/or other countries.
This Font Software is licensed under the SIL Open Font License,
Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font
creation efforts of academic and linguistic communities, and to
provide a free and open framework in which fonts may be shared and
improved in partnership with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply to
any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software
components as distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to,
deleting, or substituting -- in part or in whole -- any of the
components of the Original Version, by changing formats or by porting
the Font Software to a new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed,
modify, redistribute, and sell modified and unmodified copies of the
Font Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components, in
Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the
corresponding Copyright Holder. This restriction only applies to the
primary font name as presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created using
the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

56
addons/EGFramework/Module/EGCQRS.cs

@ -0,0 +1,56 @@ @@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
namespace EGFramework
{
#region Interface
public interface ICommand
{
void Execute();
}
public interface IQuery<TResult>
{
TResult Do();
}
public interface IEGCQRS
{
void SendCommand(ICommand command);
TResult DoQuery<TResult>(IQuery<TResult> query);
}
#endregion
public class EGCQRS :EGModule, IEGCQRS
{
public void SendCommand(ICommand command)
{
command.Execute();
}
public TResult DoQuery<TResult>(IQuery<TResult> query)
{
return query.Do();
}
public override void Init()
{
}
}
#region Extension
public static class CanSendCommandExtension
{
public static void EGSendCommand(this IEGFramework self, ICommand command)
{
EGArchitectureImplement.Interface.GetModule<EGCQRS>().SendCommand(command);
}
}
public static class CanQueryDataExtension
{
public static TResult EGQueryData<TResult>(this IEGFramework self, IQuery<TResult> query)
{
return EGArchitectureImplement.Interface.GetModule<EGCQRS>().DoQuery(query);
}
}
#endregion
}

107
addons/EGFramework/Module/EGEvent.cs

@ -0,0 +1,107 @@ @@ -0,0 +1,107 @@
using System;
using System.Collections.Generic;
namespace EGFramework
{
public interface IEGEvent{
void SendEvent<T>() where T : new();
void SendEvent<T>(T e);
IUnRegister RegisterEvent<T>(Action<T> onEvent);
void UnRegisterEvent<T>(Action<T> onEvent);
}
public class EGEvent : EGModule,IEGEvent
{
public override void Init()
{
}
private readonly EasyEvents Events = new EasyEvents();
public void SendEvent<TEvent>() where TEvent : new()
{
Events.GetEvent<EasyEvent<TEvent>>()?.Invoke(new TEvent());
}
public void SendEvent<TEvent>(TEvent e)
{
Events.GetEvent<EasyEvent<TEvent>>()?.Invoke(e);
}
public IUnRegister RegisterEvent<TEvent>(Action<TEvent> onEvent)
{
var e = Events.GetOrAddEvent<EasyEvent<TEvent>>();
return e.Register(onEvent);
}
public void UnRegisterEvent<TEvent>(Action<TEvent> onEvent)
{
var e = Events.GetEvent<EasyEvent<TEvent>>();
if (e != null)
{
e.UnRegister(onEvent);
}
}
}
public class EasyEvents
{
private static EasyEvents GlobalEvents = new EasyEvents();
public static T Get<T>() where T : IEasyEvent
{
return GlobalEvents.GetEvent<T>();
}
public static void Register<T>() where T : IEasyEvent, new()
{
GlobalEvents.AddEvent<T>();
}
private Dictionary<Type, IEasyEvent> TypeEvents = new Dictionary<Type, IEasyEvent>();
public void AddEvent<T>() where T : IEasyEvent, new()
{
TypeEvents.Add(typeof(T), new T());
}
public T GetEvent<T>() where T : IEasyEvent
{
IEasyEvent e;
if (TypeEvents.TryGetValue(typeof(T), out e))
{
return (T)e;
}
return default;
}
public T GetOrAddEvent<T>() where T : IEasyEvent, new()
{
var eType = typeof(T);
if (TypeEvents.TryGetValue(eType, out var e))
{
return (T)e;
}
var t = new T();
TypeEvents.Add(eType, t);
return t;
}
}
public static class CanRegisterEventExtension
{
public static IUnRegister EGRegisterEvent<T>(this IEGFramework self, Action<T> onEvent)
{
return EGArchitectureImplement.Interface.GetModule<EGEvent>().RegisterEvent<T>(onEvent);
}
public static void EGUnRegisterEvent<T>(this IEGFramework self, Action<T> onEvent)
{
EGArchitectureImplement.Interface.GetModule<EGEvent>().UnRegisterEvent<T>(onEvent);
}
}
public static class CanSendEventExtension
{
public static void EGSendEvent<T>(this IEGFramework self) where T : new()
{
EGArchitectureImplement.Interface.GetModule<EGEvent>().SendEvent<T>();
}
public static void EGSendEvent<T>(this IEGFramework self, T e)
{
EGArchitectureImplement.Interface.GetModule<EGEvent>().SendEvent<T>(e);
}
}
}

324
addons/EGFramework/Module/EGMessage.cs

@ -0,0 +1,324 @@ @@ -0,0 +1,324 @@
using System;
using System.Collections.Generic;
using System.Timers;
namespace EGFramework
{
//before use this module,you should install NewtonSoft.Json Nuget Package
//in vscode,you can install this package by install vscode extension NuGet Package Manager GUI
//search 'Newton' and choose Newtonsoft.Json
//EGMessage, Every Message tools will be based in this class
public class EGMessage : EGModule
{
public EasyEvent<ResponseMsg> OnDataReceived { set; get; } = new EasyEvent<ResponseMsg>();
public EasyEvent<ResponseMsgEvent> OnResponse { set; get; } = new EasyEvent<ResponseMsgEvent>();
public EasyEvent<RequestMsgEvent> OnRequest { set; get; } = new EasyEvent<RequestMsgEvent>();
/// <summary>
/// Send delay in millisecond,if you don't need a Timer to delay send message,you can set it to 0.
/// </summary>
/// <value></value>
public int SendDelay { set; get; } = 100;
public Queue<RequestMsgEvent> RequestCache { set; get; } = new Queue<RequestMsgEvent>();
private Timer RequestTimer { set; get; }
public override void Init()
{
if(SendDelay>0){
RequestTimer = new Timer(SendDelay);
RequestTimer.Elapsed += ExecuteRequest;
RequestTimer.AutoReset = true;
RequestTimer.Enabled = true;
}
}
#region ReceiveFunctions
private void ReceiveResponse<T>(ResponseMsg msg) where T :IResponse, new()
{
this.ExecuteResponse(new T(),msg.stringData,msg.byteData,msg.sender,msg.protocolType);
}
/// <summary>
/// Start to receive type of T data
/// </summary>
/// <typeparam name="T">Data class of message</typeparam>
public void OnReceive<T>() where T : IResponse, new()
{
OnDataReceived.Register(ReceiveResponse<T>);
}
/// <summary>
/// Stop to receive type of T data
/// </summary>
/// <typeparam name="T">Data class of message</typeparam>
public void OffReceive<T>() where T : IResponse, new()
{
OnDataReceived.UnRegister(ReceiveResponse<T>);
}
#endregion
#region request & response
public void SendRequest<TRequest>(TRequest request,string sender,ProtocolType protocolType) where TRequest:IRequest
{
if(SendDelay>0){
RequestCache.Enqueue(new RequestMsgEvent(request,sender,protocolType));
}else{
OnRequest.Invoke(new RequestMsgEvent(request,sender,protocolType));
}
//ExecuteRequest();
//OnRequest.Invoke(requestCache.Dequeue());
}
private void ExecuteRequest(object source, ElapsedEventArgs e){
if(RequestCache.Count>0){
OnRequest.Invoke(RequestCache.Dequeue());
}
}
private void ExecuteResponse<TResponse>(TResponse response,string protocolString,byte[] protocolBytes,string sender,ProtocolType protocolType) where TResponse:IResponse
{
bool isSet = response.TrySetData(protocolString,protocolBytes);
if (isSet)
{
//this.SendEvent(new ResponseMsgEvent(response, sender));
OnResponse.Invoke(new ResponseMsgEvent(response,sender,protocolType));
}
}
#endregion
public void SetDelay(int millisecond){
this.SendDelay = millisecond;
}
}
#region interface
public interface IResponse
{
/// <summary>
/// Attempt to fill in the data. If it does not comply with the relevant protocol rules, it is recommended to return false. If false is returned here, the data response will be ignored.
/// </summary>
/// <param name="protocolData">original received</param>
/// <returns></returns>
bool TrySetData(string protocolData,byte[] protocolBytes);
}
public interface IRequest
{
/// <summary>
/// define you message info in this function,and this return will be send to server&client by request.
/// </summary>
/// <returns>request info</returns>
string ToProtocolData();
byte[] ToProtocolByteData();
}
#endregion
#region AbstractClass
public class BaseJsonResponse : IResponse
{
private string ExceptionMsg;
public virtual string ToProtocolData()
{
return "";
}
public virtual bool TrySetData(string json,byte[] bytes)
{
try
{
return true;
}
catch (Exception e)
{
ExceptionMsg = e.ToString();
//PrintErr(ExceptionMsg);
return false;
}
}
}
public class StringRequest : IRequest
{
private string RequestStr;
public StringRequest() {
RequestStr = "No message";
}
public StringRequest(string str) {
RequestStr = str;
}
public byte[] ToProtocolByteData()
{
return null;
}
public string ToProtocolData()
{
return RequestStr;
}
}
#endregion
#region Extension
public static class CanRegisterMessageExtension {
/// <summary>
/// To register event until message received,if you only need message,please use:
/// this.RegisterMessageEvent<BaseJsonResponse>(e=>{ //To execute your message})
/// if you want to get sender,you also can to:
/// this.RegisterMessageEvent<BaseJsonResponse>((res,sender))=>{ //To execute your message
/// Print(res.toProtocolData());
/// Print(sender);
/// })
/// </summary>
/// <param name="self"></param>
/// <param name="onEvent"></param>
/// <typeparam name="TResponse"></typeparam>
/// <returns></returns>
public static IUnRegister EGRegisterMessageEvent<TResponse>(this IEGFramework self, Action<TResponse> onEvent)where TResponse : IResponse
{
return EGArchitectureImplement.Interface.GetModule<EGMessage>().OnResponse.Register(e=> {
if (e.res.GetType() == typeof(TResponse)) {
onEvent.Invoke((TResponse)e.res);
}
});
}
/// <summary>
/// To register event until message received,if you only need message,please use:
/// this.RegisterMessageEvent<BaseJsonResponse>(e=>{ //To execute your message})
/// if you want to get sender,you also can to:
/// this.RegisterMessageEvent<BaseJsonResponse>((res,sender))=>{ //To execute your message
/// Print(res.toProtocolData());
/// Print(sender);
/// })
/// </summary>
/// <param name="self"></param>
/// <param name="onEvent"></param>
/// <typeparam name="TResponse"></typeparam>
/// <returns></returns>
public static IUnRegister EGRegisterMessageEvent<TResponse>(this IEGFramework self, Action<TResponse,string> onEvent)where TResponse : IResponse
{
return EGArchitectureImplement.Interface.GetModule<EGMessage>().OnResponse.Register(e=> {
if (e.res.GetType() == typeof(TResponse)) {
onEvent.Invoke((TResponse)e.res,e.sender);
}
});
}
public static IUnRegister EGRegisterMessageEvent<TResponse>(this IEGFramework self, Action<TResponse,string,ProtocolType> onEvent)where TResponse : IResponse
{
return EGArchitectureImplement.Interface.GetModule<EGMessage>().OnResponse.Register(e=> {
if (e.res.GetType() == typeof(TResponse)) {
onEvent.Invoke((TResponse)e.res,e.sender,e.protocolType);
}
});
}
/// <summary>
/// Start to receive type of TResponse data
/// </summary>
/// <param name="self"></param>
/// <typeparam name="TResponse"></typeparam>
public static void EGOnMessage<TResponse>(this IEGFramework self) where TResponse : IResponse,new()
{
EGArchitectureImplement.Interface.GetModule<EGMessage>().OnReceive<TResponse>();
}
/// <summary>
/// Stop to receive type of TResponse data
/// </summary>
/// <param name="self"></param>
/// <typeparam name="TResponse"></typeparam>
public static void EGOffMessage<TResponse>(this IEGFramework self) where TResponse : IResponse,new()
{
EGArchitectureImplement.Interface.GetModule<EGMessage>().OffReceive<TResponse>();
}
}
public static class CanSendMessageExtension {
/// <summary>
/// to send message by request and define sender
/// </summary>
/// <param name="self"></param>
/// <param name="request"></param>
/// <param name="sender"></param>
/// <typeparam name="TRequest"></typeparam>
public static void EGSendMessage<TRequest>(this IEGFramework self, TRequest request,string sender,ProtocolType protocolType)where TRequest : IRequest
{
EGArchitectureImplement.Interface.GetModule<EGMessage>().SendRequest(request,sender,protocolType);
}
}
/// <summary>
/// this extension to link with protocol tools,such as tcp,udp,serial port,etc...
/// </summary>
public static class EGMessageEventExtension{
public static void EGOnReceivedData(this IModule self, ResponseMsg receivedData)
{
EGArchitectureImplement.Interface.GetModule<EGMessage>().OnDataReceived.Invoke(receivedData);
}
public static void EGRegisterSendAction(this IModule self, Action<RequestMsgEvent> sendAction){
EGArchitectureImplement.Interface.GetModule<EGMessage>().OnRequest.Register(sendAction);
}
}
#endregion
#region event
public struct ResponseMsg
{
public string sender;
public string stringData;
public byte[] byteData;
public ProtocolType protocolType;
public ResponseMsg(string stringData_,byte[] byteData_,string sender_,ProtocolType protocolType_)
{
stringData = stringData_;
byteData = byteData_;
sender = sender_;
protocolType = protocolType_;
}
}
public struct ResponseMsgEvent
{
public IResponse res;
public string sender;
public ProtocolType protocolType;
public ResponseMsgEvent(IResponse res_,string sender_,ProtocolType protocolType_)
{
res = res_;
sender = sender_;
protocolType = protocolType_;
}
}
public struct RequestMsgEvent
{
public IRequest req;
public string sender;
public ProtocolType protocolType;
public RequestMsgEvent(IRequest req_ ,string sender_,ProtocolType protocolType_)
{
req = req_;
sender = sender_;
protocolType = protocolType_;
}
}
#endregion
public enum ProtocolType{
TCPClient = 0x00,
TCPServer = 0x01,
UDP = 0x02,
SerialPort = 0x03,
WebSocketClient = 0x10,
WebSocketServer = 0x11,
HttpServer = 0x20,
HttpGet = 0x21,
HttpPost = 0x22,
HttpPut = 0x23,
HttpPatch = 0x24,
HttpDelete = 0x25,
DLTSClient = 0x30,
DLTSServer = 0x31,
SSLClient = 0x40,
SSLServer = 0x41,
FileStream = 0x50,
MemoryStream = 0x60,
MQTTClient = 0x70,
Bacnet = 0x80
//MQTT,SSH,etc...
}
}

65
addons/EGFramework/Module/EGObjects.cs

@ -0,0 +1,65 @@ @@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
namespace EGFramework
{
public interface IEGObject
{
void RegisterObject<T>(T object_);
T GetObject<T>() where T : class,new();
}
public class EGObject : EGModule,IEGObject
{
private IOCContainer ObjectContainer = new IOCContainer();
public override void Init()
{
}
public TObject GetObject<TObject>() where TObject : class,new()
{
if (!ObjectContainer.self.ContainsKey(typeof(TObject)))
{
this.RegisterObject(new TObject());
}
return ObjectContainer.Get<TObject>();
}
public void RegisterObject<TObject>(TObject object_)
{
ObjectContainer.Register(object_);
}
public bool ContainsObject<TObject>(){
return ObjectContainer.self.ContainsKey(typeof(TObject));
}
}
public static class CanGetObjectExtension
{
public static T EGGetObject<T>(this IEGFramework self) where T : class,new()
{
return EGArchitectureImplement.Interface.GetModule<EGObject>().GetObject<T>();
}
}
public static class CanRegisterObjectExtension
{
public static void EGRegisterObject<T>(this IArchitecture self,T object_) where T : class,new()
{
self.GetModule<EGObject>().RegisterObject(object_);
}
public static void EGRegisterObject<T>(this IEGFramework self,T object_) where T : class,new()
{
EGArchitectureImplement.Interface.GetModule<EGObject>().RegisterObject(object_);
}
}
public static class CanContainsObjectExtension{
public static bool EGContainsObject<T>(this IEGFramework self)
{
return EGArchitectureImplement.Interface.GetModule<EGObject>().ContainsObject<T>();
}
}
}

218
addons/EGFramework/Module/Extension/EGConvertExtension.cs

@ -0,0 +1,218 @@ @@ -0,0 +1,218 @@
using System;
using System.Linq;
using System.Text;
namespace EGFramework {
//协议规则解析通用方法扩展
public static class EGConvertExtension
{
/// <summary>
/// Hex string data to byte array,such as a string like "0x00 0xff 0x06"
/// </summary>
/// <param name="self">Only include A-F,0-9,hex</param>
/// <returns></returns>
public static byte[] ToByteArrayByHex(this string self) {
int hexLen = self.Length;
byte[] result;
if (hexLen % 2 == 1)
{
//奇数
hexLen++;
result = new byte[(hexLen / 2)];
self += "0" ;
}
else
{
//偶数
result = new byte[(hexLen / 2)];
}
int j = 0;
for (int i = 0; i < hexLen; i += 2)
{
result[j] = (byte)int.Parse(self.Substring(i, 2), System.Globalization.NumberStyles.HexNumber);
j++;
}
return result;
}
/// <summary>
/// get string from hex array ,like hex array {0x0a,0x11} => "0x0a 0x11"
/// </summary>
/// <param name="self"></param>
/// <returns></returns>
public static string ToStringByHex(this byte[] self)
{
StringBuilder sb = new StringBuilder();
foreach (byte b in self)
{
sb.Append(b.ToString("X2") + " ");
}
string result = sb.ToString().Trim();
return result;
}
public static string ToStringByHex0x(this byte[] self)
{
StringBuilder sb = new StringBuilder();
foreach (byte b in self)
{
sb.Append("0x" + b.ToString("X2") + " ");
}
string result = sb.ToString().Trim();
return result;
}
/// <summary>
/// get hex from string ,like string "0x0a 0x11" => {0x0a,0x11}
/// </summary>
/// <param name="self"></param>
/// <returns></returns>
public static byte[] ToHexByString(this string self)
{
string[] hexStrings = self.Split(' ');
byte[] byteArray = new byte[hexStrings.Length];
for (int i = 0; i < hexStrings.Length; i++)
{
byteArray[i] = Convert.ToByte(hexStrings[i], 16);
}
return byteArray;
}
public static byte[] ToHexByString0x(this string self)
{
if (self.Length <= 2 && self.Substring(0, 2) != "0x") {
return null;
}
return self.ToHexByString();
}
public static byte[] ToBytes(this ushort self){
byte[] byteArray = BitConverter.GetBytes(self);
if (BitConverter.IsLittleEndian)
{
Array.Reverse(byteArray);
}
return byteArray;
}
public static ushort ToUShort(this byte[] self){
if (BitConverter.IsLittleEndian)
{
Array.Reverse(self);
}
return BitConverter.ToUInt16(self, 0);
}
public static byte[] ToBytes(this uint self){
byte[] byteArray = BitConverter.GetBytes(self);
if (BitConverter.IsLittleEndian)
{
Array.Reverse(byteArray);
}
return byteArray;
}
public static uint ToUINT(this byte[] self){
if (BitConverter.IsLittleEndian)
{
Array.Reverse(self);
}
return BitConverter.ToUInt32(self, 0);
}
/// <summary>
/// convert and resize byte array,such as uint is 0x00FF7799 => byte array {0xFF,0x77,0x99}
/// </summary>
/// <param name="self"></param>
/// <returns></returns>
public static byte[] ToBytesAndResizeArray(this uint self){
byte[] byteArray = BitConverter.GetBytes(self);
if (BitConverter.IsLittleEndian)
{
Array.Reverse(byteArray);
}
int startIndex = Array.FindIndex(byteArray, b => b != 0);
if (startIndex == -1)
{
byteArray = new byte[1];
}
else
{
byteArray = byteArray.Skip(startIndex).ToArray();
}
return byteArray;
}
public static byte[] ToByteArray(this bool[] boolArray)
{
int numBool = boolArray.Length;
int numBytes = (numBool + 7) / 8;
byte[] byteArray = new byte[numBytes];
for (int i = 0; i < numBool; i++)
{
int byteIndex = i / 8;
int bitIndex = i % 8;
if (boolArray[i])
{
byteArray[byteIndex] |= (byte)(1 << bitIndex);
}
}
return byteArray;
}
public static bool[] ToBoolArray(this byte[] byteArray)
{
bool[] boolArray = new bool[byteArray.Length * 8];
for (int i = 0; i < byteArray.Length; i++)
{
byte currentByte = byteArray[i];
for (int j = 0; j < 8; j++)
{
boolArray[i * 8 + j] = (currentByte & (1 << j)) != 0;
}
}
return boolArray;
}
public static bool[] ToBoolArray(this int value)
{
string binaryString = Convert.ToString(value, 2);
bool[] boolArray = new bool[binaryString.Length];
if(binaryString.Length < 8){
boolArray = new bool[8];
}
for (int i = 0; i < binaryString.Length; i++)
{
boolArray[binaryString.Length - i - 1] = binaryString[i] == '1';
}
return boolArray;
}
public static int ToInt(this bool[] boolArray)
{
int result = 0;
for (int i = 0; i < boolArray.Length; i++)
{
if (boolArray[i])
{
result |= (1 << i);
}
}
return result;
}
public static ushort[] ToUShortArray(this byte[] byteArray){
ushort[] ushortArray = new ushort[byteArray.Length / 2];
for (int i = 0, j = 0; i < byteArray.Length; i += 2, j++)
{
ushortArray[j] = (ushort)((byteArray[i] << 8) | byteArray[i + 1]);
}
return ushortArray;
}
}
}

154
addons/EGFramework/Module/Extension/EGCrcExtension.cs

@ -0,0 +1,154 @@ @@ -0,0 +1,154 @@
using System;
using System.Security.Cryptography;
namespace EGFramework{
public static class EGCrcModbusExtension
{
/// CRC calculate is a common device verify algorithm
/// use
// hex = {0x80,0x05};
// Polynomial = x^16+x^15+x^2+1 = 1 80 05
public const ushort CRC_16_Modbus_Polynomial = 0x8005;
// hex = {0xFF,0xFF}
public const ushort CRC_16_Modbus_Start = 0xFFFF;
// hex = {0x00,0x00}
public const ushort CRC_16_Modbus_ResultXOR = 0x0000;
private static readonly ushort[] Crc_16_Table_Modbus ={
0x00,0xC0C1,0xC181,0x140,0xC301,0x3C0,0x280,0xC241,0xC601,0x6C0,0x780,0xC741,0x500,0xC5C1,0xC481,0x440,
0xCC01,0xCC0,0xD80,0xCD41,0xF00,0xCFC1,0xCE81,0xE40,0xA00,0xCAC1,0xCB81,0xB40,0xC901,0x9C0,0x880,0xC841,
0xD801,0x18C0,0x1980,0xD941,0x1B00,0xDBC1,0xDA81,0x1A40,0x1E00,0xDEC1,0xDF81,0x1F40,0xDD01,0x1DC0,0x1C80,0xDC41,
0x1400,0xD4C1,0xD581,0x1540,0xD701,0x17C0,0x1680,0xD641,0xD201,0x12C0,0x1380,0xD341,0x1100,0xD1C1,0xD081,0x1040,
0xF001,0x30C0,0x3180,0xF141,0x3300,0xF3C1,0xF281,0x3240,0x3600,0xF6C1,0xF781,0x3740,0xF501,0x35C0,0x3480,0xF441,
0x3C00,0xFCC1,0xFD81,0x3D40,0xFF01,0x3FC0,0x3E80,0xFE41,0xFA01,0x3AC0,0x3B80,0xFB41,0x3900,0xF9C1,0xF881,0x3840,
0x2800,0xE8C1,0xE981,0x2940,0xEB01,0x2BC0,0x2A80,0xEA41,0xEE01,0x2EC0,0x2F80,0xEF41,0x2D00,0xEDC1,0xEC81,0x2C40,
0xE401,0x24C0,0x2580,0xE541,0x2700,0xE7C1,0xE681,0x2640,0x2200,0xE2C1,0xE381,0x2340,0xE101,0x21C0,0x2080,0xE041,
0xA001,0x60C0,0x6180,0xA141,0x6300,0xA3C1,0xA281,0x6240,0x6600,0xA6C1,0xA781,0x6740,0xA501,0x65C0,0x6480,0xA441,
0x6C00,0xACC1,0xAD81,0x6D40,0xAF01,0x6FC0,0x6E80,0xAE41,0xAA01,0x6AC0,0x6B80,0xAB41,0x6900,0xA9C1,0xA881,0x6840,
0x7800,0xB8C1,0xB981,0x7940,0xBB01,0x7BC0,0x7A80,0xBA41,0xBE01,0x7EC0,0x7F80,0xBF41,0x7D00,0xBDC1,0xBC81,0x7C40,
0xB401,0x74C0,0x7580,0xB541,0x7700,0xB7C1,0xB681,0x7640,0x7200,0xB2C1,0xB381,0x7340,0xB101,0x71C0,0x7080,0xB041,
0x5000,0x90C1,0x9181,0x5140,0x9301,0x53C0,0x5280,0x9241,0x9601,0x56C0,0x5780,0x9741,0x5500,0x95C1,0x9481,0x5440,
0x9C01,0x5CC0,0x5D80,0x9D41,0x5F00,0x9FC1,0x9E81,0x5E40,0x5A00,0x9AC1,0x9B81,0x5B40,0x9901,0x59C0,0x5880,0x9841,
0x8801,0x48C0,0x4980,0x8941,0x4B00,0x8BC1,0x8A81,0x4A40,0x4E00,0x8EC1,0x8F81,0x4F40,0x8D01,0x4DC0,0x4C80,0x8C41,
0x4400,0x84C1,0x8581,0x4540,0x8701,0x47C0,0x4680,0x8641,0x8201,0x42C0,0x4380,0x8341,0x4100,0x81C1,0x8081,0x4040,
};
public static ushort CalculateCRC16Modbus(this byte[] bytes)
{
CRC16 provider = new CRC16(Crc_16_Table_Modbus);
byte[] hash = provider.ComputeHash(bytes);
ushort crc16 = BitConverter.ToUInt16(hash, 0);
ushort reversedResult = (ushort)((crc16 >> 8) | (crc16 << 8));
return reversedResult;
}
}
public static class EGCrcUtility
{
//Crc with table
public static uint CalculateCrc(byte[] data, uint initialValue, uint xorValue, bool inputReverse, bool outputReverse,uint[] CrcTable)
{
uint crc = initialValue;
for (int i = 0; i < data.Length; i++)
{
if (inputReverse)
data[i] = ReverseBits(data[i]);
crc ^= (uint)(data[i] << 24);
for (int j = 0; j < 8; j++)
{
crc = (crc << 8) ^ CrcTable[crc >> 24];
}
}
if (outputReverse)
crc = ReverseBits(crc);
return crc ^ xorValue;
}
//Crc without table
public static uint CalculateCrc(byte[] data, uint polynomial, uint initialValue, uint xorValue, bool inputReverse, bool outputReverse)
{
uint crc = initialValue;
for (int i = 0; i < data.Length; i++)
{
if (inputReverse)
data[i] = ReverseBits(data[i]);
crc ^= (uint)(data[i] << 24);
for (int j = 0; j < 8; j++)
{
if ((crc & 0x80000000) != 0)
{
crc = (crc << 1) ^ polynomial;
}
else
{
crc <<= 1;
}
}
}
if (outputReverse)
crc = ReverseBits(crc);
return crc ^ xorValue;
}
private static uint ReverseBits(uint value)
{
uint result = 0;
for (int i = 0; i < 8; i++)
{
result = (result << 1) | ((value >> i) & 1);
}
return result;
}
private static byte ReverseBits(byte value)
{
byte result = 0;
for (int i = 0; i < 8; i++)
{
result = (byte)((result << 1) | ((value >> i) & 1));
}
return result;
}
}
public class CRC16 : HashAlgorithm
{
private const ushort polynomial = 0x8005;
private ushort[] table = new ushort[256];
private ushort crc = 0xFFFF;
public CRC16(ushort[] table)
{
HashSizeValue = 16;
this.table = table;
}
protected override void HashCore(byte[] array, int ibStart, int cbSize)
{
for (int i = ibStart; i < ibStart + cbSize; i++)
{
byte index = (byte)(crc ^ array[i]);
crc = (ushort)((crc >> 8) ^ table[index]);
}
}
protected override byte[] HashFinal()
{
return BitConverter.GetBytes(crc);
}
public override void Initialize()
{
crc = ushort.MaxValue;
}
}
}

20
addons/EGFramework/Module/Extension/EGDateTimeExtension.cs

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
using System;
namespace EGFramework{
public static class EGDateTimeExtension
{
public static string GetFullDateMsg(this IEGFramework self)
{
return DateTime.Now.ToString("yyyy-MM-dd") + " " + DateTime.Now.ToString("HH:mm:ss");
}
public static string GetDayDateMsg(this IEGFramework self)
{
return DateTime.Now.ToString("HH:mm:ss");
}
public static long GetTimeStamp(this IEGFramework self)
{
TimeSpan ts = DateTime.Now - new DateTime(1970, 1, 1, 8, 0, 0, 0);
return System.Convert.ToInt64(ts.TotalSeconds);
}
}
}

78
addons/EGFramework/Module/Extension/EGEncodingExtension.cs

@ -0,0 +1,78 @@ @@ -0,0 +1,78 @@
using System.Text;
namespace EGFramework{
//use this extension,you should add System.Text.Encoding.CodePages package from Nuget
public static class EGEncodingExtension
{
public static bool IsInit{ set; get; }
/// <summary>
/// get encoding from encoding params(string).
/// </summary>
/// <param name="self"></param>
/// <param name="encodingTxt"></param>
/// <returns></returns>
public static Encoding GetEncoding(this IEGFramework self,string encodingTxt){
if(!IsInit){
IsInit = true;
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
}
return Encoding.GetEncoding(encodingTxt);
}
/// <summary>
/// Make a string to bytes with encoding params(string).
/// </summary>
/// <param name="self"></param>
/// <param name="encodingTxt"></param>
/// <returns></returns>
public static byte[] ToBytesByEncoding(this string self,string encodingTxt){
if(!IsInit){
IsInit = true;
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
}
return Encoding.GetEncoding(encodingTxt).GetBytes(self);
}
/// <summary>
/// Make a string to bytes with encoding.
/// </summary>
/// <param name="self"></param>
/// <param name="encoding"></param>
/// <returns></returns>
public static byte[] ToBytesByEncoding(this string self,Encoding encoding){
if(!IsInit){
IsInit = true;
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
}
return encoding.GetBytes(self);
}
/// <summary>
/// Make a bytes to string with encoding params(string).
/// </summary>
/// <param name="self"></param>
/// <param name="encodingTxt"></param>
/// <returns></returns>
public static string ToStringByEncoding(this byte[] self,string encodingTxt){
if(!IsInit){
IsInit = true;
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
}
return Encoding.GetEncoding(encodingTxt).GetString(self);
}
/// <summary>
/// Make a bytes to string with encoding.
/// </summary>
/// <param name="self"></param>
/// <param name="encodingTxt"></param>
/// <returns></returns>
public static string ToStringByEncoding(this byte[] self,Encoding encoding){
if(!IsInit){
IsInit = true;
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
}
return encoding.GetString(self);
}
}
}

54
addons/EGFramework/Module/Extension/EGIpExtension.cs

@ -0,0 +1,54 @@ @@ -0,0 +1,54 @@
namespace EGFramework {
public static class EGIpExtension
{
/// <summary>
/// Get host from IP. Such as 192.168.0.1:5555 => get 192.168.0.1
/// </summary>
/// <param name="self"></param>
/// <returns></returns>
public static string GetHostByIp(this string ip)
{
int colonIndex = ip.IndexOf(":");
string host = "";
if (colonIndex != -1)
{
host = ip.Substring(0, colonIndex);
}
return host;
}
public static int GetPortByIp(this string ip)
{
int colonIndex = ip.IndexOf(":");
string portString = ip.Substring(colonIndex + 1);
int port;
if (int.TryParse(portString, out port))
{
//nothing to do
}
else
{
port = 0;
}
return port;
}
public static string GetStrFrontSymbol(this string str,char symbol){
int colonIndex = str.IndexOf(symbol);
string frontStr = "";
if (colonIndex != -1)
{
frontStr = str.Substring(0, colonIndex);
}
return frontStr;
}
public static string GetStrBehindSymbol(this string str,char symbol){
int colonIndex = str.IndexOf(symbol);
string behindStr = str.Substring(colonIndex + 1);
return behindStr;
}
}
}

48
addons/EGFramework/Module/NodeExtension/EGCreate.cs

@ -0,0 +1,48 @@ @@ -0,0 +1,48 @@
using Godot;
using static Godot.GD;
namespace EGFramework{
public class EGSingletonNode : IEGFramework, IModule
{
public IOCContainer NodeContainer = new IOCContainer();
public void Init()
{
}
public IArchitecture GetArchitecture()
{
return EGArchitectureImplement.Interface;
}
}
public static class EGCanCreateNodeExtension
{
public static TNode CreateNode<TNode>(this Node self) where TNode : Node,new(){
TNode nodeData = new TNode();
nodeData.Name = typeof(TNode).Name;
self.AddChild(nodeData);
return nodeData;
}
public static TNode CreateNode<TNode>(this Node self,string name) where TNode : Node,new(){
TNode nodeData = new TNode();
nodeData.Name = name;
self.AddChild(nodeData);
return nodeData;
}
public static TNode SingletonNode<TNode>(this Node self) where TNode : Node,new(){
TNode nodeData = new TNode();
nodeData.Name = typeof(TNode).Name;
self.AddChild(nodeData);
return nodeData;
}
public static void Alert(this Node self,string alertMsg){
}
}
}

29
addons/EGFramework/Module/NodeExtension/EGNode.cs

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
using Godot;
using static Godot.GD;
using System;
namespace EGFramework{
public static class EGNodeExtension
{
public static TModule NodeModule<TModule>(this Node self) where TModule : Node,IModule,new(){
if(EGArchitectureImplement.Interface.IsInitModule<TModule>()){
TModule module = new TModule();
module.Name = typeof(TModule).ToString();
Print(module.Name);
self.AddChild(module);
EGArchitectureImplement.Interface.RegisterModule(module);
return module;
}else{
return EGArchitectureImplement.Interface.GetModule<TModule>();
}
}
public static void ClearChildren(this Node itemContainer)
{
foreach (Node child in itemContainer.GetChildren())
{
child.QueueFree();
}
}
}
}

9
addons/EGFramework/Module/NodeExtension/Tween/EGTween.cs

@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
using Godot;
using static Godot.GD;
namespace EGFramework{
public static class EGTween
{
}
}

299
addons/EGFramework/Module/ProtocolExtension/EGDnsExtension.cs

@ -0,0 +1,299 @@ @@ -0,0 +1,299 @@
using System;
using System.Collections.Generic;
namespace EGFramework{
/// <summary>
/// Support a class for analysis the .local(mdns) or other www.com(dns) protocol to get the message,
/// mdns protocol format type reference from https://github.com/richardschneider/net-mdns
/// nuget package is Makaretu.Dns.Multicast
/// mdns reference from https://www.rfc-editor.org/rfc/rfc6763.html
/// </summary>
public static class EGDnsExtension
{
public const string DefaultDnsServer = "1.1.1.1";
public const int DefaultDnsPort = 53;
public const string DefaultMDnsServerIpv4 = "224.0.0.251";
public const string DefaultMDnsServerIpv6 = "FF02::FB";
public const int DefaultMDnsPort = 5353;
public const string DNS_SRV_RR = "_services._dns-sd._udp.local";
}
/// <summary>
/// Dns's OpCode
/// </summary>
public enum DnsOpCode : ushort{
/// <summary>
/// Standard query.
/// </summary>
Query = 0x0000,
/// <summary>
/// Inverse query (obsolete), see https://tools.ietf.org/html/rfc3425.
/// </summary>
InverseQuery = 0x0800,
/// <summary>
/// A server status request.
/// </summary>
Status = 0x1000,
/// <summary>
/// Zone change, see https://tools.ietf.org/html/rfc1996.
/// </summary>
Notify = 0x2000,
/// <summary>
/// Update message, see https://tools.ietf.org/html/rfc2136.
/// </summary>
Update = 0x2800
}
/// <summary>
/// A resource record or query type.
/// </summary>
public enum DnsType{
/// <summary>
/// A host address.
/// </summary>
A = 1,
/// <summary>
/// An authoritative name server.
/// </summary>
NS = 2,
/// <summary>
/// The canonical name for an alias.
/// </summary>
CNAME = 5,
/// <summary>
/// Marks the start of a zone of authority.
/// </summary>
SOA = 6,
/// <summary>
/// A mailbox domain name (EXPERIMENTAL).
/// </summary>
MB = 7,
/// <summary>
/// A mail group member (EXPERIMENTAL).
/// </summary>
MG = 8,
/// <summary>
/// A mailbox rename domain name (EXPERIMENTAL).
/// </summary>
MR = 9,
/// <summary>
/// A Null resource record (EXPERIMENTAL).
/// </summary>
NULL = 10,
/// <summary>
/// A well known service description.
/// </summary>
WKS = 11,
/// <summary>
/// A domain name pointer.
/// </summary>
PTR = 12,
/// <summary>
/// Host information.
/// </summary>
HINFO = 13,
/// <summary>
/// Mailbox or mail list information.
/// </summary>
MINFO = 14,
/// <summary>
/// Mail exchange.
/// </summary>
MX = 15,
/// <summary>
/// Text resources.
/// </summary>
TXT = 16,
/// <summary>
/// Responsible Person.
/// </summary>
RP = 17,
/// <summary>
/// AFS Data Base location.
/// </summary>
AFSDB = 18,
/// <summary>
/// An IPv6 host address.
/// </summary>
AAAA = 28,
/// <summary>
/// A resource record which specifies the location of the server(s) for a specific protocol and domain.
/// </summary>
SRV = 33,
/// <summary>
/// Maps an entire domain name.
/// </summary>
DNAME = 39,
/// <summary>
/// Option record.
/// </summary>
OPT = 41,
/// <summary>
/// Delegation Signer.
/// </summary>
DS = 43,
/// <summary>
/// Signature for a RRSET with a particular name, class, and type.
/// </summary>
RRSIG = 46,
/// <summary>
/// Next secure owener.
/// </summary>
NSEC = 47,
/// <summary>
/// Public key cryptography to sign and authenticate resource records.
/// </summary>
DNSKEY = 48,
/// <summary>
/// Authenticated next secure owner.
/// </summary>
NSEC3 = 50,
/// <summary>
/// Parameters needed by authoritative servers to calculate hashed owner names.
/// </summary>
NSEC3PARAM = 51,
/// <summary>
/// Shared secret key.
/// </summary>
TKEY = 249,
/// <summary>
/// Transactional Signature.
/// </summary>
TSIG = 250,
/// <summary>
/// A request for a transfer of an entire zone.
/// </summary>
AXFR = 252,
/// <summary>
/// A request for mailbox-related records (MB, MG or MR).
/// </summary>
MAILB = 253,
/// <summary>
/// A request for any record(s).
/// </summary>
ANY = 255,
/// <summary>
/// A Uniform Resource Identifier (URI) resource record.
/// </summary>
URI = 256,
/// <summary>
/// A certification authority authorization.
/// </summary>
CAA = 257
}
/// <summary>
/// The values are maintained by IANA at https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-2.
/// </summary>
public enum DnsClass : ushort
{
/// <summary>
/// The Internet.
/// </summary>
IN = 1,
/// <summary>
/// The CSNET class (Obsolete - used only for examples insome obsolete RFCs).
/// </summary>
CS = 2,
/// <summary>
/// The CHAOS class.
/// </summary>
CH = 3,
/// <summary>
/// Hesiod[Dyer 87].
/// </summary>
HS = 4,
/// <summary>
/// Used in UPDATE message to signify no class.
/// </summary>
None = 254,
/// <summary>
/// Only used in QCLASS.
/// </summary>
ANY = 255
}
public struct DnsHead{
}
/// <summary>
/// MDns Head
/// | TransactionID (2 bytes) | OpCode (2 bytes) | Dns Sign (2 byte)
/// </summary>
public struct MDnsHead {
/// <summary>
///
/// </summary>
/// <value></value>
public ushort TransactionID { set; get; }
/// <summary>
/// The requested operation.
/// </summary>
/// <value></value>
public DnsOpCode OpCode { set; get; }
#region Sign Code
/// <summary>
/// A one bit field that specifies whether this message is a query(0), or a response(1).
/// </summary>
/// <value></value>
public bool QR { set; get; }
public bool AA { set; get; }
public bool TC { set; get; }
public bool RD { set; get; }
public bool RA { set; get; }
public byte OpCode4Bit { set; get; }
/// <summary>
/// Reserved for future use.
/// </summary>
/// <value>Must be zero in all queries and responses.</value>
public byte Z { set; get; }
/// <summary>
/// Authentic data.
/// </summary>
/// <value> true if the response data is authentic; otherwise, false.</value>
public bool AD { get; set; }
/// <summary>
/// Checking disabled.
/// </summary>
/// <value>true if the query does not require authenticated data; otherwise, false.</value>
public bool CD { get; set; }
#endregion
}
public struct DnsQuestionRequest : IRequest
{
public byte ReplyCode { set; get; }
public ushort QuestionsCount { set; get; }
public ushort AnswerRRs { set; get; }
public ushort AuthorityRRs { set; get; }
public ushort Additional { set; get; }
public List<byte[]> Data { set; get; }
public byte QuestionType { set; get; }
public byte QuestionClass { set; get; }
public byte[] ToProtocolByteData()
{
throw new NotImplementedException();
}
public string ToProtocolData()
{
throw new NotImplementedException();
}
}
}

871
addons/EGFramework/Module/ProtocolExtension/EGModbusExtension.cs

@ -0,0 +1,871 @@ @@ -0,0 +1,871 @@
using System;
using System.Linq;
using log4net.Core;
namespace EGFramework{
/// <summary>
/// some extensions function about Modbus-TCP and Modbus-RTU (Physic layer used RS-485)
/// not include Modbus-ASCII,because LRC verify not developed
/// </summary>
public static class EGModbusExtension
{
//Send Protocol
//---------Modbus-TCP's Prefix---------
//[00 00 00 00 00 06] 01 03 00 00 00 08
//00 00 ----- info head (check for reply Any things can be defined)
//xx xx 00 00 00 06 info length ( Max length 65535 )
public static byte[] MakeModbusTCPPrefix(this object self,ushort messageId,uint length){
return messageId.ToBytes().Concat(length.ToBytes()).ToArray();
}
public static byte[] MakeModbusTCPPrefix(this object self,ushort messageId,byte[] sendData){
return messageId.ToBytes().Concat(((uint)sendData.Length).ToBytes()).ToArray();
}
}
/// <summary>
/// Modbus FunctionCode
/// 0x01 => Read Coils ---- OK
/// 0x02 => Read Discrete input ---- OK
/// 0x03 => Read Holding registers ---- OK
/// 0x04 => Read Input registers ---- OK
/// 0x05 => Write Single Coils ---- OK
/// 0x06 => Write Single Holding registers ---- OK
/// 0x0F => Write Multi Coils ---- OK
/// 0x10 => Write Multi Holding registers ---- OK
/// </summary>
#region Modbus TCP Request and Response
public struct ModbusTCP_ReadCoils : IRequest
{
public const byte FunctionCode = 0x01;
public const ushort MessageId = 0xFF01;
public byte DeviceAddress { set; get; }
public ushort RegisterAddress { set; get; }
public ushort ReadCount { set; get; }
/// <summary>
/// Construct the protocol
/// </summary>
/// <param name="deviceAddress"></param>
/// <param name="registerAddress"></param>
/// <param name="readCount">Read count should be less than 2000</param>
public ModbusTCP_ReadCoils(byte deviceAddress,ushort registerAddress,ushort readCount){
DeviceAddress = deviceAddress;
RegisterAddress = registerAddress;
ReadCount = readCount;
}
public byte[] ToProtocolByteData()
{
byte[] protocolRequest = this.MakeModbusTCPPrefix(MessageId,6);
protocolRequest = protocolRequest.Append(DeviceAddress).ToArray();
protocolRequest = protocolRequest.Append(FunctionCode).ToArray();
protocolRequest = protocolRequest.Concat(RegisterAddress.ToBytes()).ToArray();
if(ReadCount>2000){
ReadCount = 2000;
}
byte[] registerValues = ReadCount.ToBytes();
protocolRequest = protocolRequest.Concat(registerValues).ToArray();
return protocolRequest;
}
public string ToProtocolData()
{
return "";
}
}
public struct ModbusTCP_ReadDiscreteInput : IRequest
{
public const byte FunctionCode = 0x02;
public const ushort MessageId = 0xFF02;
public byte DeviceAddress { set; get; }
public ushort RegisterAddress { set; get; }
public ushort ReadCount { set; get; }
/// <summary>
/// Construct the protocol
/// </summary>
/// <param name="deviceAddress"></param>
/// <param name="registerAddress"></param>
/// <param name="readCount">Read count should be less than 2000</param>
public ModbusTCP_ReadDiscreteInput(byte deviceAddress,ushort registerAddress,ushort readCount){
DeviceAddress = deviceAddress;
RegisterAddress = registerAddress;
ReadCount = readCount;
}
public byte[] ToProtocolByteData()
{
byte[] protocolRequest = this.MakeModbusTCPPrefix(MessageId,6);
protocolRequest = protocolRequest.Append(DeviceAddress).ToArray();
protocolRequest = protocolRequest.Append(FunctionCode).ToArray();
protocolRequest = protocolRequest.Concat(RegisterAddress.ToBytes()).ToArray();
if(ReadCount>2000){
ReadCount = 2000;
}
byte[] registerValues = ReadCount.ToBytes();
protocolRequest = protocolRequest.Concat(registerValues).ToArray();
return protocolRequest;
}
public string ToProtocolData()
{
return "";
}
}
public struct ModbusTCP_ReadHoldingRegisters : IRequest
{
public const byte FunctionCode = 0x03;
public const ushort MessageId = 0xFF03;
public byte DeviceAddress { set; get; }
public ushort RegisterAddress { set; get; }
public ushort ReadCount { set; get; }
/// <summary>
/// Construct the protocol
/// </summary>
/// <param name="deviceAddress"></param>
/// <param name="registerAddress"></param>
/// <param name="readCount">Read count should be less than 125</param>
public ModbusTCP_ReadHoldingRegisters(byte deviceAddress,ushort registerAddress,ushort readCount){
DeviceAddress = deviceAddress;
RegisterAddress = registerAddress;
ReadCount = readCount;
}
public byte[] ToProtocolByteData()
{
byte[] protocolRequest = this.MakeModbusTCPPrefix(MessageId,6);
protocolRequest = protocolRequest.Append(DeviceAddress).ToArray();
protocolRequest = protocolRequest.Append(FunctionCode).ToArray();
protocolRequest = protocolRequest.Concat(RegisterAddress.ToBytes()).ToArray();
if(ReadCount>125){
ReadCount = 125;
}
byte[] registerValues = ReadCount.ToBytes();
protocolRequest = protocolRequest.Concat(registerValues).ToArray();
return protocolRequest;
}
public string ToProtocolData()
{
return "";
}
}
public struct ModbusTCP_ReadInputRegisters : IRequest
{
public const byte FunctionCode = 0x04;
public const ushort MessageId = 0xFF04;
public byte DeviceAddress { set; get; }
public ushort RegisterAddress { set; get; }
public ushort ReadCount { set; get; }
/// <summary>
/// Construct the protocol
/// </summary>
/// <param name="deviceAddress"></param>
/// <param name="registerAddress"></param>
/// <param name="readCount">Read count should be less than 125</param>
public ModbusTCP_ReadInputRegisters(byte deviceAddress,ushort registerAddress,ushort readCount){
DeviceAddress = deviceAddress;
RegisterAddress = registerAddress;
ReadCount = readCount;
}
public byte[] ToProtocolByteData()
{
byte[] protocolRequest = this.MakeModbusTCPPrefix(MessageId,6);
protocolRequest = protocolRequest.Append(DeviceAddress).ToArray();
protocolRequest = protocolRequest.Append(FunctionCode).ToArray();
protocolRequest = protocolRequest.Concat(RegisterAddress.ToBytes()).ToArray();
if(ReadCount>125){
ReadCount = 125;
}
byte[] registerValues = ReadCount.ToBytes();
protocolRequest = protocolRequest.Concat(registerValues).ToArray();
return protocolRequest;
}
public string ToProtocolData()
{
return "";
}
}
public struct ModbusTCP_WriteSingleCoil : IRequest
{
public const byte FunctionCode = 0x05;
public const ushort MessageId = 0xFF05;
public byte DeviceAddress { set; get; }
public ushort RegisterAddress { set; get; }
public bool Value { set; get; }
public ModbusTCP_WriteSingleCoil(byte deviceAddress,ushort registerAddress,bool value){
DeviceAddress = deviceAddress;
RegisterAddress = registerAddress;
Value = value;
}
public byte[] ToProtocolByteData()
{
byte[] protocolRequest = this.MakeModbusTCPPrefix(MessageId,6);
protocolRequest = protocolRequest.Append(DeviceAddress).ToArray();
protocolRequest = protocolRequest.Append(FunctionCode).ToArray();
protocolRequest = protocolRequest.Concat(RegisterAddress.ToBytes()).ToArray();
byte[] registerValues = {0x00,0x00};
if(Value){
registerValues[0]=0xFF;
}
protocolRequest = protocolRequest.Concat(registerValues).ToArray();
return protocolRequest;
}
public string ToProtocolData()
{
return "";
}
}
public struct ModbusTCP_WriteSingleHoldingRegister : IRequest
{
public const byte FunctionCode = 0x06;
public const ushort MessageId = 0xFF06;
public byte DeviceAddress { set; get; }
public ushort RegisterAddress { set; get; }
public ushort Value { set; get; }
public ModbusTCP_WriteSingleHoldingRegister(byte deviceAddress,ushort registerAddress,ushort value){
DeviceAddress = deviceAddress;
RegisterAddress = registerAddress;
Value = value;
}
public byte[] ToProtocolByteData()
{
byte[] protocolRequest = this.MakeModbusTCPPrefix(MessageId,6);
protocolRequest = protocolRequest.Append(DeviceAddress).ToArray();
protocolRequest = protocolRequest.Append(FunctionCode).ToArray();
protocolRequest = protocolRequest.Concat(RegisterAddress.ToBytes()).ToArray();
byte[] registerValues = Value.ToBytes();
protocolRequest = protocolRequest.Concat(registerValues).ToArray();
return protocolRequest;
}
public string ToProtocolData()
{
return "";
}
}
public struct ModbusTCP_WriteMultiCoil : IRequest{
public const byte FunctionCode = 0x0F;
public const ushort MessageId = 0xFF0F;
public byte DeviceAddress { set; get; }
public ushort RegisterStartAddress { set; get; }
public bool[] Values { set; get; }
/// <summary>
/// Construct the protocol
/// </summary>
/// <param name="deviceAddress">usual use 0x01 for device address</param>
/// <param name="registerStartAddress"></param>
/// <param name="values">Values length should be less than 2000</param>
public ModbusTCP_WriteMultiCoil(byte deviceAddress,ushort registerStartAddress,bool[] values){
DeviceAddress = deviceAddress;
RegisterStartAddress = registerStartAddress;
Values = values;
}
public byte[] ToProtocolByteData()
{
byte[] protocolRequest = {DeviceAddress,FunctionCode};
protocolRequest = protocolRequest.Concat(RegisterStartAddress.ToBytes()).ToArray();
protocolRequest = protocolRequest.Concat(((ushort)Values.Length).ToBytes()).ToArray();
//Length range should be 1-2000 0x0001-0x07D0,otherwise delete the data after 2000
if(Values.Length>2000){
bool[] SourceValues = Values;
Values = new bool[2000];
Array.Copy(Values,0,SourceValues,0,2000);
}
//bool array 2000 => byte array 250
byte[] valueGroup = Values.ToByteArray();
byte valueLength = (byte)valueGroup.Length;
protocolRequest = protocolRequest.Append(valueLength).ToArray();
protocolRequest = protocolRequest.Concat(valueGroup).ToArray();
byte[] protocolPrefix = this.MakeModbusTCPPrefix(MessageId,protocolRequest);
protocolRequest = protocolPrefix.Concat(protocolRequest).ToArray();
return protocolRequest;
}
public string ToProtocolData()
{
return "";
}
}
/// <summary>
/// Start write at Register start address,such as 0x03,write order by order like 0x03,0x04,0x05...,write count is the value array length
/// </summary>
public struct ModbusTCP_WriteMultiHoldingRegister : IRequest
{
public const byte FunctionCode = 0x10;
public const ushort MessageId = 0xFF10;
public byte DeviceAddress { set; get; }
public ushort RegisterStartAddress { set; get; }
public ushort[] Values { set; get; }
/// <summary>
/// Construct the protocol
/// </summary>
/// <param name="deviceAddress">usual use 0x01 for device address</param>
/// <param name="registerStartAddress"></param>
/// <param name="values">Values length should be less than 125</param>
public ModbusTCP_WriteMultiHoldingRegister(byte deviceAddress,ushort registerStartAddress,ushort[] values){
DeviceAddress = deviceAddress;
RegisterStartAddress = registerStartAddress;
Values = values;
}
public byte[] ToProtocolByteData()
{
byte[] protocolRequest = {DeviceAddress,FunctionCode};
protocolRequest = protocolRequest.Concat(RegisterStartAddress.ToBytes()).ToArray();
protocolRequest = protocolRequest.Concat(((ushort)Values.Length).ToBytes()).ToArray();
//Length range should be 1-125 0x0001-0x07D,otherwise delete the data after 125
if(Values.Length>125){
ushort[] SourceValues = Values;
Values = new ushort[125];
Array.Copy(Values,0,SourceValues,0,125);
}
//ushort array 125 => byte array 250
byte[] valueGroup = {};
foreach(ushort value in Values){
byte[] registerValues = value.ToBytes();
valueGroup = valueGroup.Concat(registerValues).ToArray();
}
byte valueLength = (byte)valueGroup.Length;
protocolRequest = protocolRequest.Append(valueLength).ToArray();
protocolRequest = protocolRequest.Concat(valueGroup).ToArray();
byte[] protocolPrefix = this.MakeModbusTCPPrefix(MessageId,protocolRequest);
protocolRequest = protocolPrefix.Concat(protocolRequest).ToArray();
return protocolRequest;
}
public string ToProtocolData()
{
return "";
}
}
public struct ModbusTCP_Response : IResponse
{
public bool[] Coil { set; get; }
public bool[] DiscreteInput { set; get; }
public ushort[] HoldingRegister { set; get; }
public ushort[] InputRegister { set; get; }
public byte FunctionCode { set; get; }
public byte DeviceAddress { set; get; }
public ushort RegisterStartAddress { set; get; }
public uint DataLength { set; get; }
public byte[] SourceData { set; get; }
public ModbusFunctionType FunctionType { set; get; }
public ModbusErrorCode ErrorCode { set; get; }
public bool TrySetData(string protocolData, byte[] protocolBytes)
{
try
{
if(protocolBytes==null && protocolBytes.Length < 8){
return false;
}
SourceData = protocolBytes;
DeviceAddress = protocolBytes[6];
FunctionType = (ModbusFunctionType)protocolBytes[7];
if(FunctionCode == 0x83){
ErrorCode = (ModbusErrorCode)protocolBytes[2];
return true;
}
byte[] dataLength = new byte[4];
Array.Copy(protocolBytes,2,dataLength,0,4);
DataLength = dataLength.ToUINT();
if(protocolBytes.Length != DataLength+6){
return false;
}
//every response's start should be 0xff,because the request's start is 0xff
if(protocolBytes[0]==0xff){
switch(FunctionType){
case ModbusFunctionType.ReadCoil:
byte readCoilLength = protocolBytes[8];
byte[] CoilBytes = new byte[readCoilLength];
Array.Copy(protocolBytes,9,CoilBytes,0,readCoilLength);
Coil = CoilBytes.ToBoolArray();
return true;
case ModbusFunctionType.ReadDiscreteInput:
byte readDiscreteInputLength = protocolBytes[8];
byte[] DiscreteInputBytes = new byte[readDiscreteInputLength];
Array.Copy(protocolBytes,9,DiscreteInputBytes,0,readDiscreteInputLength);
DiscreteInput = DiscreteInputBytes.ToBoolArray();
return true;
case ModbusFunctionType.ReadHoldingRegisters:
byte readHoldingRegistersLength = protocolBytes[8];
byte[] HoldingRegistersBytes = new byte[readHoldingRegistersLength];
Array.Copy(protocolBytes,9,HoldingRegistersBytes,0,readHoldingRegistersLength);
HoldingRegister = HoldingRegistersBytes.ToUShortArray();
return true;
case ModbusFunctionType.ReadInputRegisters:
byte readInputRegistersLength = protocolBytes[8];
byte[] InputRegistersBytes = new byte[readInputRegistersLength];
Array.Copy(protocolBytes,9,InputRegistersBytes,0,readInputRegistersLength);
InputRegister = InputRegistersBytes.ToUShortArray();
return true;
case ModbusFunctionType.WriteSingleCoil:
return true;
case ModbusFunctionType.WriteSingleHoldingRegister:
return true;
case ModbusFunctionType.WriteMultiCoil:
return true;
case ModbusFunctionType.WriteMultiHoldingRegister:
return true;
default:
return false;
}
}
}
catch (Exception)
{
return false;
}
return false;
}
}
#endregion
#region Modbus RTU Request and Response,Used RS-485 for Physic layer
public struct ModbusRTU_ReadCoils : IRequest{
public const byte FunctionCode = 0x01;
public byte DeviceAddress { set; get; }
public ushort RegisterAddress { set; get; }
public ushort ReadCount { set; get; }
/// <summary>
/// Construct the protocol
/// </summary>
/// <param name="deviceAddress"></param>
/// <param name="registerAddress"></param>
/// <param name="readCount">Read count should be less than 2000</param>
public ModbusRTU_ReadCoils(byte deviceAddress,ushort registerAddress,ushort readCount){
DeviceAddress = deviceAddress;
RegisterAddress = registerAddress;
ReadCount = readCount;
}
public byte[] ToProtocolByteData()
{
byte[] protocolRequest = { DeviceAddress , FunctionCode };
protocolRequest = protocolRequest.Concat(RegisterAddress.ToBytes()).ToArray();
if(ReadCount>2000){
ReadCount = 2000;
}
byte[] registerValues = ReadCount.ToBytes();
protocolRequest = protocolRequest.Concat(registerValues).ToArray();
protocolRequest = protocolRequest.Concat(protocolRequest.CalculateCRC16Modbus().ToBytes()).ToArray();
return protocolRequest;
}
public string ToProtocolData()
{
return "";
}
}
public struct ModbusRTU_ReadDiscreteInput : IRequest{
public const byte FunctionCode = 0x02;
public byte DeviceAddress { set; get; }
public ushort RegisterAddress { set; get; }
public ushort ReadCount { set; get; }
/// <summary>
/// Construct the protocol
/// </summary>
/// <param name="deviceAddress"></param>
/// <param name="registerAddress"></param>
/// <param name="readCount">Read count should be less than 2000</param>
public ModbusRTU_ReadDiscreteInput(byte deviceAddress,ushort registerAddress,ushort readCount){
DeviceAddress = deviceAddress;
RegisterAddress = registerAddress;
ReadCount = readCount;
}
public byte[] ToProtocolByteData()
{
byte[] protocolRequest = { DeviceAddress , FunctionCode };
protocolRequest = protocolRequest.Concat(RegisterAddress.ToBytes()).ToArray();
if(ReadCount>2000){
ReadCount = 2000;
}
byte[] registerValues = ReadCount.ToBytes();
protocolRequest = protocolRequest.Concat(registerValues).ToArray();
protocolRequest = protocolRequest.Concat(protocolRequest.CalculateCRC16Modbus().ToBytes()).ToArray();
return protocolRequest;
}
public string ToProtocolData()
{
return "";
}
}
public struct ModbusRTU_ReadHoldingRegisters : IRequest{
public const byte FunctionCode = 0x03;
public byte DeviceAddress { set; get; }
public ushort RegisterAddress { set; get; }
public ushort ReadCount { set; get; }
/// <summary>
/// Construct the protocol
/// </summary>
/// <param name="deviceAddress"></param>
/// <param name="registerAddress"></param>
/// <param name="readCount">Read count should be less than 2000</param>
public ModbusRTU_ReadHoldingRegisters(byte deviceAddress,ushort registerAddress,ushort readCount){
DeviceAddress = deviceAddress;
RegisterAddress = registerAddress;
ReadCount = readCount;
}
public byte[] ToProtocolByteData()
{
byte[] protocolRequest = { DeviceAddress , FunctionCode };
protocolRequest = protocolRequest.Concat(RegisterAddress.ToBytes()).ToArray();
if(ReadCount>125){
ReadCount = 125;
}
byte[] registerValues = ReadCount.ToBytes();
protocolRequest = protocolRequest.Concat(registerValues).ToArray();
protocolRequest = protocolRequest.Concat(protocolRequest.CalculateCRC16Modbus().ToBytes()).ToArray();
return protocolRequest;
}
public string ToProtocolData()
{
return "";
}
}
public struct ModbusRTU_ReadInputRegisters : IRequest{
public const byte FunctionCode = 0x04;
public byte DeviceAddress { set; get; }
public ushort RegisterAddress { set; get; }
public ushort ReadCount { set; get; }
/// <summary>
/// Construct the protocol
/// </summary>
/// <param name="deviceAddress"></param>
/// <param name="registerAddress"></param>
/// <param name="readCount">Read count should be less than 2000</param>
public ModbusRTU_ReadInputRegisters(byte deviceAddress,ushort registerAddress,ushort readCount){
DeviceAddress = deviceAddress;
RegisterAddress = registerAddress;
ReadCount = readCount;
}
public byte[] ToProtocolByteData()
{
byte[] protocolRequest = { DeviceAddress , FunctionCode };
protocolRequest = protocolRequest.Concat(RegisterAddress.ToBytes()).ToArray();
if(ReadCount>125){
ReadCount = 125;
}
byte[] registerValues = ReadCount.ToBytes();
protocolRequest = protocolRequest.Concat(registerValues).ToArray();
protocolRequest = protocolRequest.Concat(protocolRequest.CalculateCRC16Modbus().ToBytes()).ToArray();
return protocolRequest;
}
public string ToProtocolData()
{
return "";
}
}
public struct ModbusRTU_WriteSingleCoil : IRequest{
public const byte FunctionCode = 0x05;
public byte DeviceAddress { set; get; }
public ushort RegisterAddress { set; get; }
public bool Value { set; get; }
public ModbusRTU_WriteSingleCoil(byte deviceAddress,ushort registerAddress,bool value){
DeviceAddress = deviceAddress;
RegisterAddress = registerAddress;
Value = value;
}
public byte[] ToProtocolByteData()
{
byte[] protocolRequest = { DeviceAddress , FunctionCode };
protocolRequest = protocolRequest.Concat(RegisterAddress.ToBytes()).ToArray();
byte[] registerValues = {0x00,0x00};
if(Value){
registerValues[0]=0xFF;
}
protocolRequest = protocolRequest.Concat(registerValues).ToArray();
protocolRequest = protocolRequest.Concat(protocolRequest.CalculateCRC16Modbus().ToBytes()).ToArray();
return protocolRequest;
}
public string ToProtocolData()
{
return "";
}
}
public struct ModbusRTU_WriteSingleHoldingRegister : IRequest{
public const byte FunctionCode = 0x06;
public byte DeviceAddress { set; get; }
public ushort RegisterAddress { set; get; }
public ushort Value { set; get; }
public ModbusRTU_WriteSingleHoldingRegister(byte deviceAddress,ushort registerAddress,ushort value){
DeviceAddress = deviceAddress;
RegisterAddress = registerAddress;
Value = value;
}
public byte[] ToProtocolByteData()
{
byte[] protocolRequest = { DeviceAddress , FunctionCode };
protocolRequest = protocolRequest.Concat(RegisterAddress.ToBytes()).ToArray();
byte[] registerValues = Value.ToBytes();
protocolRequest = protocolRequest.Concat(registerValues).ToArray();
protocolRequest = protocolRequest.Concat(protocolRequest.CalculateCRC16Modbus().ToBytes()).ToArray();
return protocolRequest;
}
public string ToProtocolData()
{
return "";
}
}
public struct ModbusRTU_WriteMultiCoil : IRequest{
public const byte FunctionCode = 0x0F;
public byte DeviceAddress { set; get; }
public ushort RegisterStartAddress { set; get; }
public bool[] Values { set; get; }
/// <summary>
/// Construct the protocol
/// </summary>
/// <param name="deviceAddress">usual use 0x01 for device address</param>
/// <param name="registerStartAddress"></param>
/// <param name="values">Values length should be less than 2000</param>
public ModbusRTU_WriteMultiCoil(byte deviceAddress,ushort registerStartAddress,bool[] values){
DeviceAddress = deviceAddress;
RegisterStartAddress = registerStartAddress;
Values = values;
}
public byte[] ToProtocolByteData()
{
byte[] protocolRequest = {DeviceAddress,FunctionCode};
protocolRequest = protocolRequest.Concat(RegisterStartAddress.ToBytes()).ToArray();
protocolRequest = protocolRequest.Concat(((ushort)Values.Length).ToBytes()).ToArray();
//Length range should be 1-2000 0x0001-0x07D0,otherwise delete the data after 2000
if(Values.Length>2000){
bool[] SourceValues = Values;
Values = new bool[2000];
Array.Copy(Values,0,SourceValues,0,2000);
}
//bool array 2000 => byte array 250
byte[] valueGroup = Values.ToByteArray();
byte valueLength = (byte)valueGroup.Length;
protocolRequest = protocolRequest.Append(valueLength).ToArray();
protocolRequest = protocolRequest.Concat(valueGroup).ToArray();
protocolRequest = protocolRequest.Concat(protocolRequest.CalculateCRC16Modbus().ToBytes()).ToArray();
return protocolRequest;
}
public string ToProtocolData()
{
return "";
}
}
/// <summary>
/// Start write at Register start address,such as 0x03,write order by order like 0x03,0x04,0x05...,write count is the value array length
/// </summary>
public struct ModbusRTU_WriteMultiHoldingRegister : IRequest
{
public const byte FunctionCode = 0x10;
public const ushort MessageId = 0xFF10;
public byte DeviceAddress { set; get; }
public ushort RegisterStartAddress { set; get; }
public ushort[] Values { set; get; }
/// <summary>
/// Construct the protocol
/// </summary>
/// <param name="deviceAddress">usual use 0x01 for device address</param>
/// <param name="registerStartAddress"></param>
/// <param name="values">Values length should be less than 125</param>
public ModbusRTU_WriteMultiHoldingRegister(byte deviceAddress,ushort registerStartAddress,ushort[] values){
DeviceAddress = deviceAddress;
RegisterStartAddress = registerStartAddress;
Values = values;
}
public byte[] ToProtocolByteData()
{
byte[] protocolRequest = {DeviceAddress,FunctionCode};
protocolRequest = protocolRequest.Concat(RegisterStartAddress.ToBytes()).ToArray();
protocolRequest = protocolRequest.Concat(((ushort)Values.Length).ToBytes()).ToArray();
//Length range should be 1-125 0x0001-0x07D,otherwise delete the data after 125
if(Values.Length>125){
ushort[] SourceValues = Values;
Values = new ushort[125];
Array.Copy(Values,0,SourceValues,0,125);
}
//ushort array 125 => byte array 250
byte[] valueGroup = {};
foreach(ushort value in Values){
byte[] registerValues = value.ToBytes();
valueGroup = valueGroup.Concat(registerValues).ToArray();
}
byte valueLength = (byte)valueGroup.Length;
protocolRequest = protocolRequest.Append(valueLength).ToArray();
protocolRequest = protocolRequest.Concat(valueGroup).ToArray();
protocolRequest = protocolRequest.Concat(protocolRequest.CalculateCRC16Modbus().ToBytes()).ToArray();
return protocolRequest;
}
public string ToProtocolData()
{
return "";
}
}
public struct ModbusRTU_Response : IResponse
{
public bool[] Coil { set; get; }
public bool[] DiscreteInput { set; get; }
public ushort[] HoldingRegister { set; get; }
public ushort[] InputRegister { set; get; }
public byte FunctionCode { set; get; }
public byte DeviceAddress { set; get; }
public ushort RegisterStartAddress { set; get; }
public byte[] SourceData { set; get; }
public ModbusFunctionType FunctionType { set; get; }
public ModbusErrorCode ErrorCode { set; get; }
public bool TrySetData(string protocolData, byte[] protocolBytes)
{
try
{
if(protocolBytes==null && protocolBytes.Length < 4){
return false;
}
SourceData = protocolBytes;
DeviceAddress = protocolBytes[0];
FunctionCode = protocolBytes[1];
FunctionType = (ModbusFunctionType)protocolBytes[1];
if(FunctionCode == 0x83){
ErrorCode = (ModbusErrorCode)protocolBytes[2];
return true;
}
//check crc verify is success
byte[] resultArray = new byte[protocolBytes.Length - 2];
Array.Copy(protocolBytes, 0, resultArray, 0, protocolBytes.Length - 2);
byte[] crcArray = new byte[2];
Array.Copy(protocolBytes, protocolBytes.Length - 2, crcArray, 0, 2);
if(resultArray.CalculateCRC16Modbus()!=crcArray.ToUShort()){
return false;
}
//every response's start should be 0xff,because the request's start is 0xff
switch(FunctionType){
case ModbusFunctionType.ReadCoil:
byte readCoilLength = protocolBytes[2];
byte[] CoilBytes = new byte[readCoilLength];
Array.Copy(protocolBytes,3,CoilBytes,0,readCoilLength);
Coil = CoilBytes.ToBoolArray();
return true;
case ModbusFunctionType.ReadDiscreteInput:
byte readDiscreteInputLength = protocolBytes[2];
byte[] DiscreteInputBytes = new byte[readDiscreteInputLength];
Array.Copy(protocolBytes,3,DiscreteInputBytes,0,readDiscreteInputLength);
DiscreteInput = DiscreteInputBytes.ToBoolArray();
return true;
case ModbusFunctionType.ReadHoldingRegisters:
byte readHoldingRegistersLength = protocolBytes[2];
byte[] HoldingRegistersBytes = new byte[readHoldingRegistersLength];
Array.Copy(protocolBytes,3,HoldingRegistersBytes,0,readHoldingRegistersLength);
HoldingRegister = HoldingRegistersBytes.ToUShortArray();
return true;
case ModbusFunctionType.ReadInputRegisters:
byte readInputRegistersLength = protocolBytes[2];
byte[] InputRegistersBytes = new byte[readInputRegistersLength];
Array.Copy(protocolBytes,3,InputRegistersBytes,0,readInputRegistersLength);
InputRegister = InputRegistersBytes.ToUShortArray();
return true;
case ModbusFunctionType.WriteSingleCoil:
return true;
case ModbusFunctionType.WriteSingleHoldingRegister:
return true;
case ModbusFunctionType.WriteMultiCoil:
return true;
case ModbusFunctionType.WriteMultiHoldingRegister:
return true;
default:
return false;
}
}
catch (Exception)
{
return false;
}
}
}
#endregion
public enum ModbusFunctionType{
None = 0x00,
ReadCoil = 0x01,
ReadDiscreteInput = 0x02,
ReadHoldingRegisters = 0x03,
ReadInputRegisters = 0x04,
WriteSingleCoil = 0x05,
WriteSingleHoldingRegister = 0x06,
WriteMultiCoil = 0x0f,
WriteMultiHoldingRegister = 0x10
}
public enum ModbusRegisterType{
None = 0x00,
Coil = 0x01,
DiscreteInput = 0x02,
HoldingRegister = 0x03,
InputRegisters = 0x04
}
public enum ModbusErrorCode{
NoError = 0x00,
IllegalFunction = 0x01,
IllegalDataAddress = 0x02,
IllegalDataValue = 0x03,
SlaveDeviceFailure = 0x04,
Acknowledge = 0x05,
SlaveDeviceBusy = 0x06,
MemoryParityError = 0x08,
GatewayPathUnavailable = 0x0A,
GatewayTargetDeviceFailedToRespond = 0x0B
}
}

573
addons/EGFramework/Module/ProtocolTools/EGBacnet.cs

@ -0,0 +1,573 @@ @@ -0,0 +1,573 @@
using System;
using System.Collections.Generic;
using System.IO.BACnet;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Godot;
using Newtonsoft.Json;
namespace EGFramework{
public class EGBacnet : IEGFramework, IModule,IProtocolSend,IProtocolReceived
{
public BacnetClient BacnetClient;
// All the present Bacnet Device List
public Dictionary<uint,BacnetAddress> DevicesList = new Dictionary<uint,BacnetAddress>();
public Encoding StringEncoding { set; get; } = Encoding.ASCII;
public Queue<ResponseMsg> ResponseMsgs { set; get; } = new Queue<ResponseMsg>();
public void Init()
{
this.EGRegisterSendAction(request=>{
if(request.protocolType == ProtocolType.Bacnet){
if(request.req.ToProtocolData() != "" && request.req.ToProtocolData() != null){
this.SendStringData(request.sender,request.req.ToProtocolData());
}
if(request.req.ToProtocolByteData().Length > 0 && request.req.ToProtocolByteData() != null){
this.SendByteData(request.sender,request.req.ToProtocolByteData());
}
}
});
StartIPV4();
}
public void StartIPV4(int port = 0xBAC0)
{
// Bacnet on UDP/IP/Ethernet
BacnetClient = new BacnetClient(new BacnetIpUdpProtocolTransport(port, false));
BacnetClient.Start(); // go
BacnetClient.OnIam += new BacnetClient.IamHandler(OnIam);
BacnetClient.WhoIs();
// Or Bacnet Ethernet
// bacnet_client = new BacnetClient(new BacnetEthernetProtocolTransport("Connexion au réseau local"));
// Send WhoIs in order to get back all the Iam responses :
/* Optional Remote Registration as A Foreign Device on a BBMD at @192.168.1.1 on the default 0xBAC0 port
bacnet_client.RegisterAsForeignDevice("192.168.1.1", 60);
Thread.Sleep(20);
bacnet_client.RemoteWhoIs("192.168.1.1");
*/
}
public void StartIPV6(int port = 0xBAC0){
//Bacnet on IPV6
BacnetClient = new BacnetClient(new BacnetIpV6UdpProtocolTransport(port));
BacnetClient.Start(); // go
BacnetClient.OnIam += new BacnetClient.IamHandler(OnIam);
BacnetClient.WhoIs();
}
public void StartMSTP(string serialPort = "COM4",int baudRate = 38400){
// Bacnet Mstp on COM4 à 38400 bps, own master id 8
BacnetClient = new BacnetClient(new BacnetMstpProtocolTransport(serialPort, baudRate, 8));
BacnetClient.Start(); // go
BacnetClient.OnIam += new BacnetClient.IamHandler(OnIam);
BacnetClient.WhoIs();
}
public void OnIam(BacnetClient sender, BacnetAddress adr, uint device_id, uint max_apdu, BacnetSegmentations segmentation, ushort vendor_id)
{
lock (DevicesList)
{
// Not already in the list
if(DevicesList.ContainsKey(device_id)){
DevicesList[device_id]=adr;
}else{
DevicesList.Add(device_id,adr); // add it
}
if(!IsWaitForIam){
WaitForIamResponse();
}
}
}
public bool IsWaitForIam { set; get; } = false;
public async void WaitForIamResponse(){
IsWaitForIam = true;
await Task.Delay(2000);
GD.Print("-----Get device----");
string serializeData = JsonConvert.SerializeObject(new EGBacnetWhoIsResponse(DevicesList.Keys.ToList()));
this.ResponseMsgs.Enqueue(new ResponseMsg(serializeData,StringEncoding.GetBytes(serializeData),"Who Is",ProtocolType.Bacnet));
IsWaitForIam = false;
}
public bool ReadProperty(BacnetAddress bacnetAddress, EGBacnetRequest bacnetData, out IList<BacnetValue> Value)
{
IList<BacnetValue> NoScalarValue;
Value = new List<BacnetValue>();
// Property Read
if (BacnetClient.ReadPropertyRequest(bacnetAddress, new BacnetObjectId(bacnetData.ObjectTypes, bacnetData.RegisterAddress), bacnetData.PropertyIds, out NoScalarValue)==false)
return false;
Value = NoScalarValue;
return true;
}
public bool WriteProperty(BacnetAddress bacnetAddress,EGBacnetRequest bacnetData)
{
// Property Write
BacnetValue[] NoScalarValue = { new BacnetValue(bacnetData.Value) };
//GD.Print("ValueType is " + bacnetData.ValueType+"|" +bacnetData.Value.GetType());
if (BacnetClient.WritePropertyRequest(bacnetAddress, new BacnetObjectId(bacnetData.ObjectTypes, bacnetData.RegisterAddress), bacnetData.PropertyIds, NoScalarValue) == false)
return false;
return true;
}
public bool ExecuteBacnetData(EGBacnetRequest bacnetRequest){
bool executeResult = false;
GD.Print("Execute bacnet request");
if(bacnetRequest.OperateCode == EGBacnetOperateCode.WhoIsRequest){
GD.Print("Request for Who Is");
BacnetClient.WhoIs();
return true;
}
if(!DevicesList.ContainsKey(bacnetRequest.DeviceId)){
return false;
}
BacnetAddress address = DevicesList[bacnetRequest.DeviceId];
IList<BacnetValue> bacnetValueQuery = null;
switch(bacnetRequest.OperateCode){
case EGBacnetOperateCode.ReadPropertyRequest:
executeResult = ReadProperty(address,bacnetRequest,out bacnetValueQuery);
break;
case EGBacnetOperateCode.WritePropertyRequest:
executeResult = WriteProperty(address,bacnetRequest);
break;
}
EGBacnetResponse response = new EGBacnetResponse(bacnetRequest,bacnetValueQuery);
response.IsSuccess = executeResult;
string serializeData = JsonConvert.SerializeObject(response);
this.ResponseMsgs.Enqueue(new ResponseMsg(serializeData,StringEncoding.GetBytes(serializeData),bacnetRequest.DeviceId.ToString(),ProtocolType.Bacnet));
return executeResult;
}
public EGBacnetWhoIsResponse WhoIs(){
return new EGBacnetWhoIsResponse(DevicesList.Keys.ToList());
}
public EGBacnetResponse ReadRegisterProperty(EGBacnetRequest bacnetRequest){
bool executeResult = false;
try
{
BacnetAddress address = DevicesList[bacnetRequest.DeviceId];
IList<BacnetValue> bacnetValueQuery = null;
switch(bacnetRequest.OperateCode){
case EGBacnetOperateCode.ReadPropertyRequest:
executeResult = ReadProperty(address,bacnetRequest,out bacnetValueQuery);
break;
}
GD.Print(bacnetValueQuery[0].Value.GetType());
EGBacnetResponse response = new EGBacnetResponse(bacnetRequest,bacnetValueQuery);
response.IsSuccess = executeResult;
return response;
}
catch (System.Exception e)
{
EGBacnetResponse response = new EGBacnetResponse(bacnetRequest);
response.IsSuccess = executeResult;
response.FailedReason = e.ToString();
return response;
}
}
public EGBacnetResponse WriteRegisterProperty(EGBacnetRequest bacnetRequest){
bool executeResult = false;
try
{
if(bacnetRequest.ValueType != BacnetApplicationTags.BACNET_APPLICATION_TAG_NULL){
bacnetRequest.Value = bacnetRequest.Value.ConvertBacnetValueType(bacnetRequest.ValueType);
}
GD.Print(bacnetRequest.Value.GetType());
BacnetAddress address = DevicesList[bacnetRequest.DeviceId];
switch(bacnetRequest.OperateCode){
case EGBacnetOperateCode.WritePropertyRequest:
executeResult = WriteProperty(address,bacnetRequest);
break;
}
EGBacnetResponse response = new EGBacnetResponse(bacnetRequest);
response.IsSuccess = executeResult;
return response;
}
catch (System.Exception e)
{
EGBacnetResponse response = new EGBacnetResponse(bacnetRequest);
response.IsSuccess = executeResult;
response.FailedReason = e.ToString();
return response;
}
}
public EGBacnetResponseReadMulti ReadRegisterMulti(EGBacnetRequestReadMulti bacnetRequest){
bool executeResult = false;
try
{
if(!DevicesList.ContainsKey(bacnetRequest.DeviceId)){
return new EGBacnetResponseReadMulti(){
FailedReason = "Device " + bacnetRequest.DeviceId + " is offline!"
};
}
BacnetAddress address = DevicesList[bacnetRequest.DeviceId];
List<EGBacnetRegisterValueInfo> resultSet = new List<EGBacnetRegisterValueInfo>();
switch(bacnetRequest.OperateCode){
case EGBacnetOperateCode.ReadMultiRequest:
foreach(EGBacnetRegisterInfo info in bacnetRequest.RegisterInfos){
EGBacnetRegisterValueInfo result = ReadRegisterOne(address,info);
resultSet.Add(result);
}
executeResult = true;
break;
}
EGBacnetResponseReadMulti response = new EGBacnetResponseReadMulti(resultSet);
response.IsSuccess = executeResult;
return response;
}
catch (System.Exception e)
{
return new EGBacnetResponseReadMulti(){
FailedReason = e.ToString()
};
}
}
public EGBacnetRegisterValueInfo ReadRegisterOne(BacnetAddress bacnetAddress, EGBacnetRegisterInfo registerInfo){
EGBacnetRegisterValueInfo value = new EGBacnetRegisterValueInfo(){
ObjectTypes = registerInfo.ObjectTypes,
RegisterAddress = registerInfo.RegisterAddress,
PropertyIds = registerInfo.PropertyIds
};
IList<BacnetValue> NoScalarValue;
try
{
bool IsSuccess = BacnetClient.ReadPropertyRequest(bacnetAddress, new BacnetObjectId(registerInfo.ObjectTypes, registerInfo.RegisterAddress), registerInfo.PropertyIds, out NoScalarValue);
if(IsSuccess){
value.IsSuccess = true;
value.Value = NoScalarValue[0];
}
}
catch (System.Exception e)
{
GD.Print(e);
throw;
}
return value;
}
public void SendByteData(string destination, byte[] data)
{
try
{
string DataJson = StringEncoding.GetString(data);
SendStringData(destination,DataJson);
}
catch (System.Exception)
{
throw;
}
}
public void SendStringData(string destination, string data)
{
try
{
EGBacnetRequest bacnetData = JsonConvert.DeserializeObject<EGBacnetRequest>(data);
ExecuteBacnetData(bacnetData);
}
catch (System.Exception e)
{
GD.PrintErr(e);
throw;
}
}
public void SetEncoding(Encoding textEncoding)
{
this.StringEncoding = textEncoding;
}
public Queue<ResponseMsg> GetReceivedMsg()
{
return this.ResponseMsgs;
}
public IArchitecture GetArchitecture()
{
return EGArchitectureImplement.Interface;
}
}
#region Who Is
public class EGBacnetWhoIsResponse : IResponse,IRequest
{
public string FunctionCode { set; get; } = "WhoIsResponse";
public List<uint> DevicesList = new List<uint>();
public EGBacnetWhoIsResponse(){
}
public EGBacnetWhoIsResponse(List<uint> devicesList){
this.DevicesList = devicesList;
}
public bool TrySetData(string protocolData, byte[] protocolBytes)
{
try
{
EGBacnetWhoIsResponse response = JsonConvert.DeserializeObject<EGBacnetWhoIsResponse>(protocolData);
if(response.FunctionCode != FunctionCode){
return false;
}
this.DevicesList = response.DevicesList;
//throw new System.NotImplementedException();
return true;
}
catch (System.Exception e)
{
GD.PrintErr("Who is error:"+ e);
return false;
}
}
public string ToProtocolData()
{
return JsonConvert.SerializeObject(this,Formatting.Indented);
}
public byte[] ToProtocolByteData()
{
return null;
}
}
#endregion
#region Read&Write One Register
public class EGBacnetRequest : IRequest,IResponse{
public string FunctionCode { set; get; } = "OperateRequest";
public EGBacnetOperateCode OperateCode { set; get; }
public uint DeviceId { set; get; }
public BacnetObjectTypes ObjectTypes { set; get; }
public uint RegisterAddress { set; get; }
public BacnetPropertyIds PropertyIds { set; get; }
public object Value { set; get; }
public BacnetApplicationTags ValueType { set; get; }
public byte[] ToProtocolByteData()
{
return null;
}
public string ToProtocolData()
{
return JsonConvert.SerializeObject(this,Formatting.Indented);
}
public bool TrySetData(string protocolData, byte[] protocolBytes)
{
try
{
//GD.Print("Init Request");
EGBacnetRequest request = JsonConvert.DeserializeObject<EGBacnetRequest>(protocolData);
if(request.FunctionCode != FunctionCode){
return false;
}
//GD.Print("OperateCode is " + request.OperateCode);
this.OperateCode = request.OperateCode;
this.DeviceId = request.DeviceId;
this.ObjectTypes = request.ObjectTypes;
this.RegisterAddress = request.RegisterAddress;
this.PropertyIds = request.PropertyIds;
this.Value = request.Value;
this.ValueType = request.ValueType;
return true;
}
catch (System.Exception e)
{
GD.PrintErr(e);
return false;
}
}
}
public class EGBacnetResponse : IResponse,IRequest
{
public string FunctionCode { set; get; } = "OperateResponse";
public bool IsSuccess { set; get; } = false;
public EGBacnetRequest Request { set; get; }
public IList<BacnetValue> ValueQuery { set; get; }
public string FailedReason { set; get; }
public EGBacnetResponse(){
}
public EGBacnetResponse(EGBacnetRequest request){
this.Request = request;
}
public EGBacnetResponse(EGBacnetRequest request,IList<BacnetValue> values){
this.Request = request;
this.ValueQuery = values;
}
public bool TrySetData(string protocolData, byte[] protocolBytes)
{
try
{
EGBacnetResponse response = JsonConvert.DeserializeObject<EGBacnetResponse>(protocolData);
if(response.FunctionCode != FunctionCode){
return false;
}
this.IsSuccess = response.IsSuccess;
this.Request = response.Request;
this.ValueQuery = response.ValueQuery;
return true;
}
catch (System.Exception)
{
return false;
}
//throw new System.NotImplementedException();
}
public string ToProtocolData()
{
return JsonConvert.SerializeObject(this,Formatting.Indented);
}
public byte[] ToProtocolByteData()
{
return null;
}
}
#endregion
#region Multi Operate
public class EGBacnetRequestReadMulti : IRequest,IResponse{
public string FunctionCode { set; get; } = "ReadMultiRequest";
public EGBacnetOperateCode OperateCode { set; get; }
public uint DeviceId { set; get; }
public List<EGBacnetRegisterInfo> RegisterInfos { set; get; } = new List<EGBacnetRegisterInfo>();
public byte[] ToProtocolByteData()
{
return null;
}
public string ToProtocolData()
{
return JsonConvert.SerializeObject(this,Formatting.Indented);
}
public bool TrySetData(string protocolData, byte[] protocolBytes)
{
try
{
//GD.Print("Init Request");
EGBacnetRequestReadMulti request = JsonConvert.DeserializeObject<EGBacnetRequestReadMulti>(protocolData);
if(request.FunctionCode != FunctionCode){
return false;
}
//GD.Print("OperateCode is " + request.OperateCode);
this.OperateCode = request.OperateCode;
this.DeviceId = request.DeviceId;
this.RegisterInfos = request.RegisterInfos;
return true;
}
catch (System.Exception e)
{
GD.PrintErr(e);
return false;
}
}
}
public class EGBacnetResponseReadMulti : IResponse,IRequest
{
public string FunctionCode { set; get; } = "ReadMultiResponse";
public bool IsSuccess { set; get; } = false;
public List<EGBacnetRegisterValueInfo> RegisterInfos { set; get; } = new List<EGBacnetRegisterValueInfo>();
public string FailedReason { set; get; }
public EGBacnetResponseReadMulti(){}
public EGBacnetResponseReadMulti(List<EGBacnetRegisterValueInfo> valueSet){
this.RegisterInfos = valueSet;
}
public bool TrySetData(string protocolData, byte[] protocolBytes)
{
try
{
EGBacnetResponseReadMulti response = JsonConvert.DeserializeObject<EGBacnetResponseReadMulti>(protocolData);
if(response.FunctionCode != FunctionCode){
FailedReason = "[Error] FunctionCode is error!";
return false;
}
this.IsSuccess = response.IsSuccess;
this.RegisterInfos = response.RegisterInfos;
this.FailedReason = response.FailedReason;
return true;
}
catch (System.Exception e)
{
GD.Print(e);
return false;
}
//throw new System.NotImplementedException();
}
public string ToProtocolData()
{
return JsonConvert.SerializeObject(this,Formatting.Indented);
}
public byte[] ToProtocolByteData()
{
return null;
}
}
public struct EGBacnetRegisterInfo{
public BacnetObjectTypes ObjectTypes { set; get; }
public uint RegisterAddress { set; get; }
public BacnetPropertyIds PropertyIds { set; get; }
}
public struct EGBacnetRegisterValueInfo{
public BacnetObjectTypes ObjectTypes { set; get; }
public uint RegisterAddress { set; get; }
public BacnetPropertyIds PropertyIds { set; get; }
public bool IsSuccess { set; get; }
public BacnetValue Value{ set; get; }
}
#endregion
public enum EGBacnetOperateCode{
ReadPropertyRequest = 0,
WritePropertyRequest = 1,
WhoIsRequest = 2,
ReadMultiRequest = 3,
}
public static class CanGetEGBacnetExtension{
public static EGBacnet EGBacnet(this IEGFramework self){
return self.GetModule<EGBacnet>();
}
public static object ConvertBacnetValueType(this object value,BacnetApplicationTags valueType){
object resultValue = value;
switch(valueType){
case BacnetApplicationTags.BACNET_APPLICATION_TAG_BOOLEAN:
resultValue = Convert.ToBoolean(value);
break;
case BacnetApplicationTags.BACNET_APPLICATION_TAG_UNSIGNED_INT:
resultValue = Convert.ToUInt32(value);
break;
case BacnetApplicationTags.BACNET_APPLICATION_TAG_SIGNED_INT:
resultValue = Convert.ToInt32(value);
break;
case BacnetApplicationTags.BACNET_APPLICATION_TAG_REAL:
resultValue = Convert.ToSingle(value);
break;
case BacnetApplicationTags.BACNET_APPLICATION_TAG_DOUBLE:
resultValue = Convert.ToDouble(value);
break;
}
return resultValue;
}
}
}

100
addons/EGFramework/Module/ProtocolTools/EGFileStream.cs

@ -0,0 +1,100 @@ @@ -0,0 +1,100 @@
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace EGFramework{
public class EGFileStream : IEGFramework, IModule,IProtocolSend,IProtocolReceived
{
public Encoding StringEncoding { set; get; } = Encoding.UTF8;
public Queue<ResponseMsg> ResponseMsgs { set; get; } = new Queue<ResponseMsg>();
public void Init()
{
this.EGRegisterSendAction(request=>{
if(request.protocolType == ProtocolType.FileStream){
if(request.req.ToProtocolData() != "" && request.req.ToProtocolData() != null){
this.SendStringData(request.sender,request.req.ToProtocolData());
}
if(request.req.ToProtocolByteData().Length > 0 && request.req.ToProtocolByteData() != null){
this.SendByteData(request.sender,request.req.ToProtocolByteData());
}
}
});
}
public IArchitecture GetArchitecture()
{
return EGArchitectureImplement.Interface;
}
public async void SendByteData(string destination, byte[] data)
{
string path = destination;
try
{
if (File.Exists(path))
{
File.Delete(path);
}
FileStream fileStream = File.Create(path);
await fileStream.WriteAsync(data,0,data.Length);
fileStream.Close();
await fileStream.DisposeAsync();
}
catch (System.Exception)
{
throw;
}
//throw new System.NotImplementedException();
}
public void SendStringData(string destination, string data)
{
SendByteData(destination,StringEncoding.GetBytes(data));
//throw new System.NotImplementedException();
}
/// <summary>
/// Read file by FileStream and return received a byte data or string data from file
/// </summary>
/// <param name="path">File Path</param>
public async void ReadFromFile(string path){
try
{
FileStream fileStream = new FileStream(path,FileMode.Open);
byte[] buffer = new byte[fileStream.Length];
await fileStream.ReadAsync(buffer, 0, (int)fileStream.Length);
fileStream.Close();
await fileStream.DisposeAsync();
string data = StringEncoding.GetString(buffer);
ResponseMsg receivedMsgs = new ResponseMsg(data,buffer,path, ProtocolType.FileStream);
ResponseMsgs.Enqueue(receivedMsgs);
}
catch (System.Exception e)
{
Godot.GD.Print("e:" + e);
throw;
}
}
public void SetEncoding(Encoding textEncoding)
{
this.StringEncoding = textEncoding;
}
public Queue<ResponseMsg> GetReceivedMsg()
{
return this.ResponseMsgs;
}
}
public static class CanGetEGFileStreamExtension{
public static EGFileStream EGFileStream(this IEGFramework self){
return self.GetModule<EGFileStream>();
}
public static void EGReadFromFile(this IEGFramework self,string path){
self.GetModule<EGFileStream>().ReadFromFile(path);
}
}
}

96
addons/EGFramework/Module/ProtocolTools/EGHttpClient.cs

@ -0,0 +1,96 @@ @@ -0,0 +1,96 @@
using System;
using System.Collections.Generic;
using System.Drawing.Printing;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace EGFramework{
public class EGHttpClient : IEGFramework, IModule
{
public HttpClient HTTPClient { set; get; } = new HttpClient();
public Encoding StringEncoding { set; get; } = Encoding.UTF8;
public void Init()
{
}
public async Task<TResponse> HttpRequest<TResponse>(string hostUrl,IRequest request,ProtocolType protocolType = ProtocolType.HttpGet,EGFormData formData = null) where TResponse : IResponse,new(){
// Call asynchronous network methods in a try/catch block to handle exceptions.
try
{
HttpContent httpContent = new StringContent("",Encoding.UTF8,"application/json");
if(formData != null){
MultipartFormDataContent formContent = new MultipartFormDataContent();
foreach(KeyValuePair<string,string> pair in formData.FormStrings){
formContent.Add(new StringContent(pair.Value,Encoding.UTF8),pair.Key);
}
foreach(KeyValuePair<string,byte[]> pair in formData.FormBytes){
formContent.Add(new ByteArrayContent(pair.Value),pair.Key,pair.Key+"."+formData.Suffix);
}
foreach(KeyValuePair<string,Stream> pair in formData.FormStreams){
formContent.Add(new StreamContent(pair.Value),pair.Key,pair.Key+"."+formData.Suffix);
}
httpContent = formContent;
}
else if(request.ToProtocolData() != null && request.ToProtocolData() != ""){
httpContent = new StringContent(request.ToProtocolData(),Encoding.UTF8,"application/json");
}else if (request.ToProtocolByteData() != null){
httpContent = new ByteArrayContent(request.ToProtocolByteData());
}
HttpResponseMessage httpResponse;
switch(protocolType){
case ProtocolType.HttpGet:
httpResponse = await HTTPClient.GetAsync(hostUrl);
break;
case ProtocolType.HttpPost:
httpResponse = await HTTPClient.PostAsync(hostUrl,httpContent);
break;
case ProtocolType.HttpPut:
httpResponse = await HTTPClient.PutAsync(hostUrl,httpContent);
break;
case ProtocolType.HttpPatch:
httpResponse = await HTTPClient.PatchAsync(hostUrl,httpContent);
break;
case ProtocolType.HttpDelete:
httpResponse = await HTTPClient.DeleteAsync(hostUrl);
break;
default:
httpResponse = await HTTPClient.GetAsync(hostUrl);
break;
}
httpResponse.EnsureSuccessStatusCode();
byte[] responseBytes = await httpResponse.Content.ReadAsByteArrayAsync();
string responseBody = await httpResponse.Content.ReadAsStringAsync();
TResponse response = new TResponse();
response.TrySetData(responseBody,responseBytes);
return response;
}
catch (HttpRequestException e)
{
Godot.GD.Print("Exception Message : "+e.Message);
return default;
}
}
public IArchitecture GetArchitecture()
{
return EGArchitectureImplement.Interface;
}
}
public class EGFormData{
public string Suffix = "";
public Dictionary<string,string> FormStrings = new Dictionary<string, string>();
public Dictionary<string,byte[]> FormBytes = new Dictionary<string, byte[]>();
public Dictionary<string,System.IO.Stream> FormStreams = new Dictionary<string, System.IO.Stream>();
}
public static class CanGetEGHttpClientExtension{
public static EGHttpClient EGHttpClient(this IEGFramework self){
return self.GetModule<EGHttpClient>();
}
}
}

148
addons/EGFramework/Module/ProtocolTools/EGHttpServer.cs

@ -0,0 +1,148 @@ @@ -0,0 +1,148 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace EGFramework{
public class EGHttpServer : IEGFramework, IModule
{
public HttpListener HttpServer { set; get; }
public Encoding StringEncoding { set; get; } = Encoding.UTF8;
public Dictionary<string,HttpListenerResponse> ResponsePools { set; get; } = new Dictionary<string, HttpListenerResponse>();
public Dictionary<string,Func<ResponseMsg,IRequest>> ExecuteDelegates { set; get; } = new Dictionary<string, Func<ResponseMsg, IRequest>>();
public void Init()
{
}
/// <summary>
/// if you are in Win7 or newest system, you should add prefix in urlacl by cmd for example: netsh http add urlacl url=http://+:6555/index/ user=Everyone
/// </summary>
/// <param name="prefix"></param>
/// <returns></returns>
public async void Listen(string prefix,Func<ResponseMsg,IRequest> responseFunc){
ExecuteDelegates.Add(prefix,responseFunc);
if(!HttpListener.IsSupported){
return;
}
if(HttpServer == null){
HttpServer = new HttpListener();
}
HttpServer.Prefixes.Add(prefix);
if(!HttpServer.IsListening){
HttpServer.Start();
//GD.Print("Http listened in :" + prefix);
while(true){
HttpListenerContext context = await HttpServer.GetContextAsync();
HttpListenerRequest request = context.Request;
string responseKey = "";
ResponseMsg receivedMsgs = new ResponseMsg();
try
{
switch (request.HttpMethod)
{
case "POST":
{
Stream stream = context.Request.InputStream;
StreamReader reader = new StreamReader(stream, Encoding.UTF8);
// byte[] postBuffer = new byte[stream.Length];
// stream.Read(postBuffer,0,postBuffer.Length);
string postString = reader.ReadToEnd();
receivedMsgs = new ResponseMsg(postString,new byte[1],"POST", ProtocolType.HttpServer);
}
break;
case "GET":
{
NameValueCollection data = request.QueryString;
Dictionary<string,string> getDic = data.AllKeys.ToDictionary(k=>k,k=>data[k]);
string getData = JsonConvert.SerializeObject(getDic);
byte[] getBuffer = StringEncoding.GetBytes(getData);
responseKey = "GET:"+this.GetTimeStamp().ToString();
receivedMsgs = new ResponseMsg(getData,getBuffer,responseKey, ProtocolType.HttpServer);
//GD.Print("Received from "+receivedMsgs.sender+": "+receivedMsgs.stringData);
}
break;
case "PUT":
{
Stream stream = context.Request.InputStream;
StreamReader reader = new StreamReader(stream, Encoding.UTF8);
byte[] postBuffer = new byte[stream.Length];
stream.Read(postBuffer,0,postBuffer.Length);
string postData = reader.ReadToEnd();
receivedMsgs = new ResponseMsg(postData,postBuffer,"PUT", ProtocolType.HttpServer);
}
break;
case "PATCH":
{
Stream stream = context.Request.InputStream;
StreamReader reader = new StreamReader(stream, Encoding.UTF8);
byte[] postBuffer = new byte[stream.Length];
stream.Read(postBuffer,0,postBuffer.Length);
string postData = reader.ReadToEnd();
receivedMsgs = new ResponseMsg(postData,postBuffer,"PATCH", ProtocolType.HttpServer);
}
break;
case "DELETE":
{
NameValueCollection data = request.QueryString;
Dictionary<string,string> getDic = data.AllKeys.ToDictionary(k=>k,k=>data[k]);
string getData = JsonConvert.SerializeObject(getDic);
byte[] getBuffer = StringEncoding.GetBytes(getData);
receivedMsgs = new ResponseMsg(getData,getBuffer,"DELETE", ProtocolType.HttpServer);
}
break;
}
}
catch (System.Exception e)
{
receivedMsgs.stringData = "Exception:"+e;
throw;
}
HttpListenerResponse response = context.Response;
response.AppendHeader("Access-Control-Allow-Origin", "*");
response.AppendHeader("Access-Control-Allow-Credentials", "true");
response.AppendHeader("Server", "MyIIS");
response.StatusCode = 200;
byte[] buffer = StringEncoding.GetBytes("API not found!");
if(ExecuteDelegates.ContainsKey(request.Url.ToString()+"/")){
IRequest result = ExecuteDelegates[request.Url.ToString()+"/"].Invoke(receivedMsgs);
buffer = StringEncoding.GetBytes(result.ToProtocolData());
}
System.IO.Stream output = response.OutputStream;
await output.WriteAsync(buffer, 0, buffer.Length);
output.Close();
// if(!ResponsePools.ContainsKey("")){
// ResponsePools.Add("",response);
// }
// Response("","Hello world");
}
}
Godot.GD.Print("Server Overed");
}
public IArchitecture GetArchitecture()
{
return EGArchitectureImplement.Interface;
}
}
public static class CanGetEGHttpServerExtension{
public static EGHttpServer EGHttpServer(this IEGFramework self){
return self.GetModule<EGHttpServer>();
}
public static void EGHttpServerListen(this IEGFramework self ,string prefix,Func<ResponseMsg,IRequest> responseFunc){
self.GetModule<EGHttpServer>().Listen(prefix,responseFunc);
}
}
}

163
addons/EGFramework/Module/ProtocolTools/EGMQTT.cs

@ -0,0 +1,163 @@ @@ -0,0 +1,163 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Godot;
using MQTTnet;
using MQTTnet.Client;
//Important:
//This EGModule implement by the nuget package MQTTnet:
//the project url is: https://github.com/dotnet/MQTTnet
//license is : https://github.com/dotnet/MQTTnet/blob/master/LICENSE by MIT license
namespace EGFramework{
public class EGMqtt : IEGFramework, IModule, IProtocolSend, IProtocolReceived
{
public MqttFactory MqttFactory = new MqttFactory();
public Dictionary<string,IMqttClient> MqttDevices { set; get; } = new Dictionary<string, IMqttClient>();
public Encoding StringEncoding { set; get; } = Encoding.UTF8;
public Queue<ResponseMsg> ResponseMsgs { set; get; } = new Queue<ResponseMsg>();
public EasyEvent<string> OnMqttConnect { set; get; } = new EasyEvent<string>();
public void Init()
{
this.EGRegisterSendAction(request=>{
if(request.protocolType == ProtocolType.MQTTClient){
if(request.req.ToProtocolData() != null && request.req.ToProtocolData() != ""){
this.SendStringData(request.sender.GetStrFrontSymbol('|'),request.sender.GetStrBehindSymbol('|'),request.req.ToProtocolData());
}
if(request.req.ToProtocolByteData() != null && request.req.ToProtocolByteData().Length > 0){
this.SendByteData(request.sender.GetStrFrontSymbol('|'),request.sender.GetStrBehindSymbol('|'),request.req.ToProtocolByteData());
}
}
});
}
public async void ConnectMQTTServer(string serverURL){
if(!MqttDevices.ContainsKey(serverURL)){
IMqttClient mqttClient = MqttFactory.CreateMqttClient();
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer(serverURL).Build();
mqttClient.ApplicationMessageReceivedAsync += e =>
{
byte[] receivedBytes = e.ApplicationMessage.PayloadSegment.ToArray();
ResponseMsgs.Enqueue(new ResponseMsg(StringEncoding.GetString(receivedBytes),receivedBytes,serverURL + "|" + e.ApplicationMessage.Topic,ProtocolType.MQTTClient));
//GD.Print(e.ApplicationMessage.Topic+":"+e.ApplicationMessage.PayloadSegment.ToArray().ToStringByHex());
return Task.CompletedTask;
};
await mqttClient.ConnectAsync(mqttClientOptions,CancellationToken.None);
MqttDevices.Add(serverURL,mqttClient);
GD.Print("Success Connect!"+MqttDevices[serverURL].IsConnected);
OnMqttConnect.Invoke(serverURL);
}else{
if(!MqttDevices[serverURL].IsConnected){
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer(serverURL).Build();
await MqttDevices[serverURL].ConnectAsync(mqttClientOptions,CancellationToken.None);
GD.Print("Success Connect!"+MqttDevices[serverURL].IsConnected);
OnMqttConnect.Invoke(serverURL);
}else{
GD.Print("Server has been Connected"+MqttDevices[serverURL].IsConnected);
OnMqttConnect.Invoke(serverURL);
}
}
}
public async void DisconnectMQTTServer(string serverURL){
if(MqttDevices.ContainsKey(serverURL) && MqttDevices[serverURL].IsConnected){
await MqttDevices[serverURL].DisconnectAsync(new MqttClientDisconnectOptionsBuilder().WithReason(MqttClientDisconnectOptionsReason.NormalDisconnection).Build());
}else{
GD.Print("Not connect");
}
}
public async void SubScribeTheme(string serverURL,string Theme){
MqttClientSubscribeOptions mqttSubscribeOptions = MqttFactory.CreateSubscribeOptionsBuilder()
.WithTopicFilter(
f =>
{
f.WithTopic(Theme);
})
.Build();
if(MqttDevices.ContainsKey(serverURL) && MqttDevices[serverURL].IsConnected){
await MqttDevices[serverURL].SubscribeAsync(mqttSubscribeOptions,CancellationToken.None);
GD.Print("Subscribe "+Theme+" success!");
}else{
GD.Print("Not connect");
}
}
public async void UnSubScribeTheme(string serverURL,string Theme){
MqttClientUnsubscribeOptions mqttUnSubscribeOptions = MqttFactory.CreateUnsubscribeOptionsBuilder()
.WithTopicFilter(Theme)
.Build();
if(MqttDevices.ContainsKey(serverURL) && MqttDevices[serverURL].IsConnected){
await MqttDevices[serverURL].UnsubscribeAsync(mqttUnSubscribeOptions,CancellationToken.None);
}
}
public async void PublishTheme(string serverURL,string Theme,string Data){
var applicationMessage = new MqttApplicationMessageBuilder()
.WithTopic(Theme)
.WithPayload(Data)
.Build();
if(MqttDevices.ContainsKey(serverURL) && MqttDevices[serverURL].IsConnected){
await MqttDevices[serverURL].PublishAsync(applicationMessage, CancellationToken.None);
}
}
public async void PublishTheme(string serverURL,string Theme,byte[] Data){
var applicationMessage = new MqttApplicationMessageBuilder()
.WithTopic(Theme)
.WithPayload(Data)
.Build();
if(MqttDevices.ContainsKey(serverURL) && MqttDevices[serverURL].IsConnected){
await MqttDevices[serverURL].PublishAsync(applicationMessage, CancellationToken.None);
GD.Print("publish success!");
}
}
public void SendByteData(string serverURL,string theme, byte[] data)
{
this.PublishTheme(serverURL,theme,data);
}
public void SendByteData(string destination, byte[] data)
{
this.SendByteData(destination.GetStrFrontSymbol('|'),destination.GetStrBehindSymbol('|'),data);
}
public void SendStringData(string serverURL,string theme, string data)
{
this.PublishTheme(serverURL,theme,data);
}
public void SendStringData(string destination, string data)
{
this.SendStringData(destination.GetStrFrontSymbol('|'),destination.GetStrBehindSymbol('|'),data);
}
public void SetEncoding(Encoding textEncoding)
{
StringEncoding = textEncoding;
}
public Queue<ResponseMsg> GetReceivedMsg()
{
return ResponseMsgs;
}
public IArchitecture GetArchitecture()
{
return EGArchitectureImplement.Interface;
}
}
public static class CanGetEGMqttExtension{
public static EGMqtt EGMqtt(this IEGFramework self){
return self.GetModule<EGMqtt>();
}
}
}

140
addons/EGFramework/Module/ProtocolTools/EGModbus.cs

@ -0,0 +1,140 @@ @@ -0,0 +1,140 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Godot;
namespace EGFramework{
/// <summary>
/// This class is not a simple protocol tools,only support sample functions to easy use.
/// </summary>
public class EGModbus : IEGFramework, IModule
{
public Queue<ModbusRTU_Response> RTUCache = new Queue<ModbusRTU_Response>();
public Queue<ModbusTCP_Response> TCPCache = new Queue<ModbusTCP_Response>();
public int Delay = 2000;
public Queue<int> WaitForSendRTU = new Queue<int>();
public int NextSendRTU = 0;
public int SendPointerRTU = 1;
public EasyEvent OnReadTimeOut = new EasyEvent();
public void Init()
{
this.EGRegisterMessageEvent<ModbusRTU_Response>((e,sender,ProtocolType)=>{
if(ProtocolType == ProtocolType.SerialPort){
RTUCache.Enqueue(e);
}
});
this.EGRegisterMessageEvent<ModbusTCP_Response>((e,sender,ProtocolType)=>{
if(ProtocolType == ProtocolType.TCPClient){
TCPCache.Enqueue(e);
}
});
this.EGOnMessage<ModbusRTU_Response>();
this.EGOnMessage<ModbusTCP_Response>();
}
public bool IsReadingRTU { set; get; }
public async Task<ModbusRTU_Response?> ReadRTUAsync(ModbusRegisterType registerType,string serialPort,byte deviceAddress,ushort start,ushort count){
if(IsReadingRTU){
SendPointerRTU++;
int messageId = SendPointerRTU;
WaitForSendRTU.Enqueue(messageId);
await Task.Run(async () =>
{
while (IsReadingRTU || NextSendRTU != messageId)
{
await Task.Delay(10);
//break;
}
});
GD.Print("-----Read"+messageId+" ----");
//return null;
}
RTUCache.Clear();
IsReadingRTU = true;
IRequest ReadRequest;
ModbusRTU_Response? res = null;
switch(registerType){
case ModbusRegisterType.HoldingRegister:
ReadRequest = new ModbusRTU_ReadHoldingRegisters(deviceAddress,start,count);
// this.AppendMessage("【发送-"+DataModbusItem.SerialPort+"】 "+ReadRequest.ToProtocolByteData().ToStringByHex());
this.EGSendMessage(ReadRequest,serialPort,ProtocolType.SerialPort);
// this.EGSerialPort().SetExpectReceivedDataLength(5+count*2);
this.EGSerialPort().SetExpectReceivedDataLength(5);
break;
}
await Task.Run(async ()=>{
int timeout = 0;
while(RTUCache.Count==0 && timeout < Delay){
await Task.Delay(10);
timeout+=10;
}
if(RTUCache.Count>0){
res = RTUCache.Dequeue();
}else{
//Print Error Timeout
OnReadTimeOut.Invoke();
this.EGSerialPort().ClearBuffer(serialPort);
}
});
IsReadingRTU = false;
if(this.WaitForSendRTU.Count>0){
NextSendRTU = this.WaitForSendRTU.Dequeue();
}
return res;
}
public bool IsReadingTCP { set; get; }
public async Task<ModbusTCP_Response?> ReadTCPAsync(ModbusRegisterType registerType,string ipPort,byte deviceAddress,ushort start,ushort count){
if(IsReadingTCP){
await Task.Run(async () =>
{
while (IsReadingTCP)
{
await Task.Delay(10);
//break;
}
});
//return null;
}
TCPCache.Clear();
IsReadingTCP = true;
IRequest ReadRequest;
ModbusTCP_Response? res = null;
switch(registerType){
case ModbusRegisterType.HoldingRegister:
ReadRequest = new ModbusTCP_ReadHoldingRegisters(deviceAddress,start,count);
// this.AppendMessage("【发送-"+DataModbusItem.SerialPort+"】 "+ReadRequest.ToProtocolByteData().ToStringByHex());
this.EGSendMessage(ReadRequest,ipPort,ProtocolType.TCPClient);
break;
}
await Task.Run(async ()=>{
int timeout = 0;
while(TCPCache.Count == 0 && timeout < Delay){
await Task.Delay(10);
timeout += 10;
}
if(TCPCache.Count>0){
res = TCPCache.Dequeue();
}else{
//Print Error Timeout
OnReadTimeOut.Invoke();
}
});
IsReadingTCP = false;
return res;
}
public IArchitecture GetArchitecture()
{
return EGArchitectureImplement.Interface;
}
}
public static class CanGetEGModbusExtension{
public static EGModbus EGModbus(this IEGFramework self){
return self.GetModule<EGModbus>();
}
}
}

71
addons/EGFramework/Module/ProtocolTools/EGProtocolSchedule.cs

@ -0,0 +1,71 @@ @@ -0,0 +1,71 @@
using Godot;
using System;
using System.Collections.Generic;
namespace EGFramework{
/// <summary>
/// In Godot engine, the async method return cannot operate the godot main thread such as Screen Trees Node.
/// The protocol schedule provide a main thread to check the message in protocol tools 's response message.
/// If you have more idea ,please issue to me, thanks.
/// </summary>
public partial class EGProtocolSchedule : Node, IModule,IEGFramework
{
public Dictionary<Type,IProtocolReceived> ProtocolTools = new Dictionary<Type, IProtocolReceived>();
public void Init()
{
}
public override void _Process(double delta)
{
foreach(IProtocolReceived tool in ProtocolTools.Values){
if(tool.GetReceivedMsg().Count>0){
this.GetModule<EGMessage>().OnDataReceived.Invoke(tool.GetReceivedMsg().Dequeue());
}
}
}
public void EnabledAllTools(){
this.EnabledTool<EGTCPClient>();
this.EnabledTool<EGTCPServer>();
this.EnabledTool<EGUDP>();
this.EnabledTool<EGSerialPort>();
this.EnabledTool<EGFileStream>();
}
public void EnabledTool<TProtocolReceived>() where TProtocolReceived : class, IModule,IProtocolReceived,new(){
if(!ProtocolTools.ContainsKey(typeof(TProtocolReceived))){
ProtocolTools.Add(typeof(TProtocolReceived),this.GetModule<TProtocolReceived>());
}
}
public void DisabledAllTools(){
ProtocolTools.Clear();
}
public void DisabledTool<TProtocolReceived>() where TProtocolReceived : class, IModule,IProtocolReceived,new(){
if(ProtocolTools.ContainsKey(typeof(TProtocolReceived))){
ProtocolTools.Remove(typeof(TProtocolReceived));
}
}
public IArchitecture GetArchitecture()
{
return EGArchitectureImplement.Interface;
}
}
public static class CanGetEGProtocolExtension{
public static EGProtocolSchedule EGProtocolSchedule(this Node self){
return self.NodeModule<EGProtocolSchedule>();
}
public static void EGEnabledProtocolTools(this Node self){
self.NodeModule<EGProtocolSchedule>().EnabledAllTools();
}
public static void EGEnabledProtocolTool<TProtocolReceived>(this Node self) where TProtocolReceived : class, IModule,IProtocolReceived,new(){
self.NodeModule<EGProtocolSchedule>().EnabledTool<TProtocolReceived>();
}
}
}

216
addons/EGFramework/Module/ProtocolTools/EGSerialPort.cs

@ -0,0 +1,216 @@ @@ -0,0 +1,216 @@
using System.IO.Ports;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace EGFramework{
public class EGSerialPort : IModule,IEGFramework,IProtocolSend,IProtocolReceived
{
public Dictionary<string,SerialPort> SerialPortDevices { set; get; } = new Dictionary<string, SerialPort>();
public Dictionary<string,int> SerialPortMapping { set; get; } = new Dictionary<string, int>();
public int DefaultBaudRate { set; get; } = 115200;
// If data pack break,you can set this param to control the data pack completed at least.
private int MinDataPackLength { set; get; } = 0;
public string ErrorLogs { set; get; }
public string ReceivedStr { set; get; } = "";
public Encoding StringEncoding { set; get; } = Encoding.UTF8;
public Queue<ResponseMsg> ResponseMsgs { set; get; } = new Queue<ResponseMsg>();
public void Init()
{
this.EGRegisterSendAction(request=>{
if(request.protocolType == ProtocolType.SerialPort){
if(request.req.ToProtocolData() != "" && request.req.ToProtocolData() != null){
this.SendSerialStringData(request.sender,request.req.ToProtocolData());
}
if(request.req.ToProtocolByteData().Length > 0 && request.req.ToProtocolByteData() != null){
this.SendSerialByteData(request.sender,request.req.ToProtocolByteData());
}
}
});
}
public Queue<ResponseMsg> GetReceivedMsg()
{
return this.ResponseMsgs;
}
public void SetEncoding(Encoding encoding){
this.StringEncoding = encoding;
}
public Encoding GetEncoding(){
return StringEncoding;
}
public List<string> RefreshSerialPort(){
string[] portNames = SerialPort.GetPortNames();
SerialPortMapping.Clear();
int index = 0;
foreach (string portName in portNames)
{
SerialPortMapping.Add(portName,index);
index++;
}
return SerialPortMapping.Keys.ToList();
}
public void SetBaudRate(int baudRate){
this.DefaultBaudRate = baudRate;
}
/// <summary>
/// Open serial port with check if target serial port isOpen.
/// </summary>
/// <param name="serialPort"></param>
public bool Open(string serialPort){
try{
if(!SerialPortDevices.ContainsKey(serialPort)){
SerialPort newPort = new SerialPort(serialPort, DefaultBaudRate, Parity.None, 8, StopBits.One);
SerialPortDevices.Add(serialPort,newPort);
if (!SerialPortDevices[serialPort].IsOpen)
{
SerialPortDevices[serialPort].Open();
SerialPortDevices[serialPort].DataReceived += SerialPort_DataReceived;
}
}else{
if(!SerialPortDevices[serialPort].IsOpen){
SerialPortDevices[serialPort].Open();
}
}
return true;
}
catch(Exception e){
ErrorLogs = "[open port error]" + e.ToString();
return false;
}
}
/// <summary>
/// Close serial port with check if target serial port isOpen.
/// </summary>
/// <param name="serialPort"></param>
public void Close(string serialPort){
if(SerialPortDevices.ContainsKey(serialPort)){
if (SerialPortDevices[serialPort].IsOpen)
{
SerialPortDevices[serialPort].Close();
SerialPortDevices.Remove(serialPort);
}
}else{
//Not found in SerialPortDevices,need add?
}
}
public bool SendSerialByteData(string serialPort,byte[] data){
// if serial port not open,open first
try{
bool isSuccessPort = Open(serialPort);
if(isSuccessPort){
SerialPortDevices[serialPort].Write(data, 0, data.Length);
return true;
}else{
return false;
}
}catch(Exception e){
ErrorLogs = "[write error]" + e.ToString();
return false;
}
}
public void SendByteData(string destination,byte[] data){
SendSerialByteData(destination,data);
}
public bool SendByteDataOnce(string serialPort,byte[] data){
bool isSuccessSend = SendSerialByteData(serialPort,data);
if(isSuccessSend){
SerialPortDevices[serialPort].Close();
SerialPortDevices.Remove(serialPort);
}
return isSuccessSend;
}
public bool SendSerialStringData(string serialPort,string data){
// if serial port not open,open first
try{
bool isSuccessPort = Open(serialPort);
if(isSuccessPort){
byte[] encodingData = StringEncoding.GetBytes(data);
SerialPortDevices[serialPort].Write(encodingData, 0, encodingData.Length);
return true;
}else{
return false;
}
}catch(Exception e){
ErrorLogs = "[write error]" + e.ToString();
return false;
}
}
public void SendStringData(string destination,string data){
SendSerialStringData(destination,data);
}
public bool SendStringDataOnce(string serialPort,string data){
bool isSuccessSend = SendSerialStringData(serialPort,data);
if(isSuccessSend){
SerialPortDevices[serialPort].Close();
SerialPortDevices.Remove(serialPort);
}
return isSuccessSend;
}
private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
//await Task.Run(() => {}).ConfigureAwait(false);
SerialPort serialPort = (SerialPort)sender;
if(serialPort.BytesToRead >= MinDataPackLength){
int bufferSize = serialPort.BytesToRead;
byte[] buffer = new byte[bufferSize];
serialPort.Read(buffer,0,serialPort.BytesToRead);
string str = StringEncoding.GetString(buffer);
Godot.GD.Print("[Receive]"+buffer.ToStringByHex());
ResponseMsgs.Enqueue(new ResponseMsg(str,buffer,serialPort.PortName,ProtocolType.SerialPort));
MinDataPackLength = 0;
//this.EGOnReceivedData(new ResponseMsg(str,buffer,serialPort.PortName,ProtocolType.SerialPort));
}else{
Godot.GD.Print("[Data Get]" + serialPort.BytesToRead);
}
}
public void SetExpectReceivedDataLength(int leastLength){
this.MinDataPackLength = leastLength;
}
public void ClearBuffer(string serialPort){
if(SerialPortDevices.ContainsKey(serialPort)){
if (SerialPortDevices[serialPort].IsOpen)
{
SerialPortDevices[serialPort].DiscardInBuffer();
}
}else{
//Not found in SerialPortDevices,need add?
}
}
public IArchitecture GetArchitecture()
{
return EGArchitectureImplement.Interface;
}
}
public static class CanGetEGSerialPortExtension{
public static EGSerialPort EGSerialPort(this IEGFramework self){
return self.GetModule<EGSerialPort>();
}
public static SerialPort EGGetSerialPort(this IEGFramework self,string serial){
return self.GetModule<EGSerialPort>().SerialPortDevices[serial];
}
}
}

181
addons/EGFramework/Module/ProtocolTools/EGTCPClient.cs

@ -0,0 +1,181 @@ @@ -0,0 +1,181 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Threading.Tasks;
using System.Net;
namespace EGFramework{
public class EGTCPClient : IModule, IEGFramework, IProtocolSend, IProtocolReceived
{
public Dictionary<string,TcpClient> TCPClientDevices { set; get; } = new Dictionary<string, TcpClient>();
public string ErrorLogs { set; get; }
public Encoding StringEncoding { set; get; } = Encoding.UTF8;
public Queue<ResponseMsg> ResponseMsgs { set; get; } = new Queue<ResponseMsg>();
public void Init()
{
this.EGRegisterSendAction(request=>{
if(request.protocolType == ProtocolType.TCPClient){
if(request.req.ToProtocolData() != null && request.req.ToProtocolData() != ""){
this.SendStringData(request.sender.GetHostByIp(),request.sender.GetPortByIp(),request.req.ToProtocolData());
}
if(request.req.ToProtocolByteData() != null && request.req.ToProtocolByteData().Length > 0){
this.SendByteData(request.sender.GetHostByIp(),request.sender.GetPortByIp(),request.req.ToProtocolByteData());
}
}
});
}
/// <summary>
/// Connect Tcp client to server with check if target server is listened.
/// </summary>
public async Task<bool> ConnectTCP(string host,int port){
try{
if(!TCPClientDevices.ContainsKey(host + ":" + port)){
TcpClient tcpClient = new TcpClient();
await tcpClient.ConnectAsync(host,port);
//Print("Connect Tcp success in "+tcpClient.Client.RemoteEndPoint.ToString());
TCPClientDevices.Add(host + ":" + port,tcpClient);
_ = HandleClientAsync(tcpClient,host,port);
}else{
if(!TCPClientDevices[host + ":" + port].Connected){
await TCPClientDevices[host + ":" + port].ConnectAsync(host,port);
_ = HandleClientAsync(TCPClientDevices[host + ":" + port],host,port);
}
}
return true;
}
catch(Exception e){
ErrorLogs = "[open port error]" + e.ToString();
return false;
}
}
/// <summary>
/// Disconnect Tcp client to server.
/// </summary>
public void DisconnectTCP(string host,int port){
if(TCPClientDevices.ContainsKey(host + ":" + port)){
if (TCPClientDevices[host + ":" + port].Connected)
{
TCPClientDevices[host + ":" + port].Close();
TCPClientDevices.Remove(host + ":" + port);
}
}else{
//Not found in TCPClientDevices,need add?
}
}
public void SetEncoding(Encoding textEncoding)
{
this.StringEncoding = textEncoding;
}
public async void SendByteData(string host,int port,byte[] data){
// if serial port not open,open first
try{
bool result = await ConnectTCP(host,port);
if(result){
await TCPClientDevices[host + ":" + port].GetStream().WriteAsync(data,0,data.Length);
}
}catch(Exception e){
ErrorLogs = "[write error]" + e.ToString();
}
}
public void SendByteData(string destination,byte[] data){
SendByteData(destination.GetHostByIp(),destination.GetPortByIp(),data);
}
public void SendByteDataOnce(string host,int port,byte[] data){
SendByteData(host,port,data);
DisconnectTCP(host,port);
}
public void SendStringData(string host,int port,string str){
SendByteData(host,port,StringEncoding.GetBytes(str));
}
public void SendStringData(string destination,string data){
SendStringData(destination.GetHostByIp(),destination.GetPortByIp(),data);
}
public void SendStringDataOnce(string host,int port,string str){
SendStringData(host,port,str);
DisconnectTCP(host,port);
}
public Queue<ResponseMsg> GetReceivedMsg()
{
return ResponseMsgs;
}
/// <summary>
/// UpdateStatus
/// </summary>
public async void CheckAndRelink(){
foreach(TcpClient tcpClient in TCPClientDevices.Values){
if(!tcpClient.Connected){
await tcpClient.ConnectAsync((IPEndPoint)tcpClient.Client.RemoteEndPoint);
}
}
}
public async Task HandleClientAsync(TcpClient client,string host,int port)
{
try
{
NetworkStream stream = client.GetStream();
string ClientName = host+":"+port;
while (true)
{
byte[] buffer = new byte[1024];
int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
if (bytesRead == 0)
{
break;
}
string data = StringEncoding.GetString(buffer, 0, bytesRead);
byte[] receivedByte = new byte[bytesRead];
Array.Copy(buffer, 0, receivedByte, 0, bytesRead);
ResponseMsg receivedMsgs = new ResponseMsg(data,receivedByte,ClientName, ProtocolType.TCPClient);
ResponseMsgs.Enqueue(receivedMsgs);
//this.EGOnReceivedData(receivedMsgs);
}
DeleteClient(client,host,port);
}
catch (Exception)
{
}
}
public void DeleteClient(TcpClient client,string host,int port)
{
client.Close();
string clientName = host+":"+port;
if (TCPClientDevices.ContainsKey(clientName)) {
TCPClientDevices.Remove(clientName);
}
}
public IArchitecture GetArchitecture()
{
return EGArchitectureImplement.Interface;
}
}
public static class CanGetEGTCPClientExtension{
public static EGTCPClient EGTCPClient(this IEGFramework self){
return self.GetModule<EGTCPClient>();
}
public static TcpClient EGGetTCPClient(this IEGFramework self,string host,int port){
return self.GetModule<EGTCPClient>().TCPClientDevices[host + ":" + port];
}
}
}

138
addons/EGFramework/Module/ProtocolTools/EGTCPServer.cs

@ -0,0 +1,138 @@ @@ -0,0 +1,138 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace EGFramework{
public class EGTCPServer : IModule, IEGFramework,IProtocolReceived
{
public TcpListener TcpServer { set; get; }
public bool IsListening { set; get; }
public Dictionary<string, TcpClient> LinkedClients { set; get; } = new Dictionary<string, TcpClient>();
public Encoding StringEncoding { set; get; } = Encoding.UTF8;
public List<string> ClientNames = new List<string>();
public EasyEvent<string> OnClientConnect { set; get; } = new EasyEvent<string>();
public EasyEvent<string> OnClientDisconnect { set; get; } = new EasyEvent<string>();
public Queue<ResponseMsg> ResponseMsgs { set; get; } = new Queue<ResponseMsg>();
public string ErrorLogs { set; get; }
public void Init()
{
this.EGRegisterSendAction(request => {
if(request.protocolType == ProtocolType.TCPServer){
if(request.req.ToProtocolData() != null && request.req.ToProtocolData() != ""){
ResponseStringData(request.sender,request.req.ToProtocolData());
}
if(request.req.ToProtocolByteData() != null && request.req.ToProtocolByteData().Length > 0){
ResponseByteData(request.sender,request.req.ToProtocolByteData());
}
}
});
}
public Queue<ResponseMsg> GetReceivedMsg()
{
return ResponseMsgs;
}
public async void StartServer(int port)
{
IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, port);
TcpServer = new(ipEndPoint);
try
{
TcpServer.Start();
IsListening = true;
while (IsListening)
{
TcpClient client = await TcpServer.AcceptTcpClientAsync();
ClientNames.Add(client.Client.RemoteEndPoint.ToString());
LinkedClients.Add(client.Client.RemoteEndPoint.ToString(), client);
OnClientConnect.Invoke(client.Client.RemoteEndPoint.ToString());
_ = HandleClientAsync(client);
}
TcpServer.Stop();
}
catch (Exception)
{
}
}
public async Task HandleClientAsync(TcpClient client)
{
try
{
NetworkStream stream = client.GetStream();
string ClientName = client.Client.RemoteEndPoint.ToString();
while (true)
{
byte[] buffer = new byte[1024];
int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
if (bytesRead == 0)
{
break;
}
string data = StringEncoding.GetString(buffer, 0, bytesRead);
ResponseMsg receivedMsgs = new ResponseMsg(data,buffer,ClientName, ProtocolType.TCPServer);
//await Task.Run(() => OnDataReceived(receivedMsgs)).ConfigureAwait(false);
//this.EGOnReceivedData(receivedMsgs);
ResponseMsgs.Enqueue(receivedMsgs);
}
//await Task.Run(() => DeleteClient(client)).ConfigureAwait(false);
DeleteClient(client);
client.Close();
}
catch (Exception)
{
}
}
public async void ResponseByteData(string clientName,byte[] data){
// if serial port not open,open first
try{
await this.LinkedClients[clientName]?.GetStream().WriteAsync(data, 0, data.Length);
}catch(Exception e){
ErrorLogs = "[write error]" + e.ToString();
}
}
public void ResponseStringData(string clientName,string str){
byte[] buffer = StringEncoding.GetBytes(str);
ResponseByteData(clientName,buffer);
}
public void DeleteClient(TcpClient client)
{
string clientName = client.Client.RemoteEndPoint.ToString();
if (ClientNames.Contains(clientName)) {
ClientNames.Remove(clientName);
}
if (LinkedClients.ContainsKey(clientName)) {
LinkedClients.Remove(clientName);
}
OnClientDisconnect.Invoke(clientName);
}
public IArchitecture GetArchitecture()
{
return EGArchitectureImplement.Interface;
}
}
public static class CanGetEGTCPServerExtension{
public static EGTCPServer EGTCPServer(this IEGFramework self){
return self.GetModule<EGTCPServer>();
}
public static void EGTCPServerListen(this IEGFramework self ,int port){
self.GetModule<EGTCPServer>().StartServer(port);
}
}
}

123
addons/EGFramework/Module/ProtocolTools/EGUDP.cs

@ -0,0 +1,123 @@ @@ -0,0 +1,123 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace EGFramework{
public class EGUDP : IEGFramework, IModule, IProtocolSend, IProtocolReceived
{
public Dictionary<int,UdpClient> UDPDevices { set; get; } = new Dictionary<int, UdpClient>();
public Encoding StringEncoding { set; get; } = Encoding.UTF8;
public Queue<ResponseMsg> ResponseMsgs { set; get; } = new Queue<ResponseMsg>();
public void Init()
{
this.EGRegisterSendAction(request => {
if(request.protocolType == ProtocolType.UDP){
if(request.req.ToProtocolData() != null && request.req.ToProtocolData() != ""){
this.SendStringData(request.sender.GetHostByIp(),request.sender.GetPortByIp(),request.req.ToProtocolData());
}
if(request.req.ToProtocolByteData() != null && request.req.ToProtocolByteData().Length > 0){
this.SendByteData(request.sender.GetHostByIp(),request.sender.GetPortByIp(),request.req.ToProtocolByteData());
}
}
});
}
public void ListenUDP(int localPort){
if (!UDPDevices.ContainsKey(localPort)) {
try
{
UdpClient udpDevice = new UdpClient(localPort);
UDPDevices.Add(localPort,udpDevice);
HandleUDPListenAsync(udpDevice);
//StartListening(localPort);
}
catch (Exception e){
Godot.GD.Print("Error" + e);
}
}
}
public void EndListenUDP(int localPort){
if (UDPDevices.ContainsKey(localPort)) {
UDPDevices[localPort].Close();
UDPDevices.Remove(localPort);
}
}
public async void HandleUDPListenAsync(UdpClient client)
{
try
{
while (true)
{
UdpReceiveResult data = await client.ReceiveAsync();
string dataStr = StringEncoding.GetString(data.Buffer);
ResponseMsg receivedMsgs = new ResponseMsg(dataStr,data.Buffer,data.RemoteEndPoint.ToString(), ProtocolType.UDP);
//this.EGOnReceivedData(receivedMsgs);
ResponseMsgs.Enqueue(receivedMsgs);
}
}
catch (Exception)
{
}
}
public void SendByteData(string host,int port,byte[] data){
UdpClient udpClient = new UdpClient();
try{
udpClient.Send(data, data.Length, host, port);
}
catch ( Exception e ){
Godot.GD.Print(e.ToString());
}
udpClient.Close();
udpClient.Dispose();
}
public void SendByteData(string destination,byte[] data){
SendByteData(destination.GetHostByIp(),destination.GetPortByIp(),data);
}
public void SendStringData(string host,int port,string data){
byte[] buffer = StringEncoding.GetBytes(data);
this.SendByteData(host,port,buffer);
}
public void SendStringData(string destination,string data){
SendStringData(destination.GetHostByIp(),destination.GetPortByIp(),data);
}
public void SetEncoding(Encoding textEncoding){
StringEncoding = textEncoding;
}
public void BroadCastUDPMessage(string host,int port,byte[] message){
}
public IArchitecture GetArchitecture()
{
return EGArchitectureImplement.Interface;
}
public Queue<ResponseMsg> GetReceivedMsg()
{
return ResponseMsgs;
}
}
public static class CanGetEGUDPExtension{
public static EGUDP EGUDP(this IEGFramework self){
return self.GetModule<EGUDP>();
}
public static void EGUDPListen(this IEGFramework self ,int port){
self.GetModule<EGUDP>().ListenUDP(port);
}
}
}

19
addons/EGFramework/Module/ProtocolTools/ProtocolToolsInterface.cs

@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
using System.Collections.Generic;
using System.Text;
namespace EGFramework{
public interface IProtocolSend{
public void SendByteData(string destination,byte[] data);
public void SendStringData(string destination,string data);
public void SetEncoding(Encoding textEncoding);
}
public interface IProtocolReceived{
public Queue<ResponseMsg> GetReceivedMsg();
}
public interface IProtocolListener{
public bool IsEnabled(string ServiceName);
}
}

247
addons/EGFramework/Module/SQL/EGSqlite.cs

@ -0,0 +1,247 @@ @@ -0,0 +1,247 @@
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Data.Sqlite;
using Newtonsoft.Json;
namespace EGFramework{
public interface IEGSqlite{
void SaveData<TData>(TData data) where TData : new();
List<TData> GetDataSet<TData>() where TData : new();
void InitDatabase(string dataBaseName);
}
public class EGSqlite : EGModule,IEGSqlite
{
public string DBName = "Default";
private string DefaultDBFolder = "SaveData";
public SqliteConnection SqliteConn;
public string ExceptionMsg;
public override void Init()
{
if (!Directory.Exists(DefaultDBFolder))
{
Directory.CreateDirectory(DefaultDBFolder);
}
InitDatabase(DBName);
}
public void InitDatabase(string dataBaseName)
{
SqliteConn = new SqliteConnection("Data Source="+DefaultDBFolder+"/"+dataBaseName+".db;Mode=ReadWriteCreate;"); // Open the connection:
try
{
SqliteConn.Open();
}
catch (Exception ex)
{
ExceptionMsg = ex.ToString();
}
}
//Save data to default sqlite database;
public void SaveData<TData>(TData data) where TData : new()
{
// if table is not exist, create table and insert data to table,else insert into data to table
if(IsTableExist<TData>()){
InsertData(data);
}else{
CreateTable<TData>();
InsertData(data);
}
}
/// <summary>
/// Get data from table where named type of TData
/// </summary>
/// <typeparam name="TData">Table name</typeparam>
/// <returns></returns>
public List<TData> GetDataSet<TData>() where TData : new()
{
// query dataSet from table TData_List
List<TData> dataSet = new List<TData>();
if(IsTableExist<TData>()){
dataSet = SelectData<TData>();
}else{
ExceptionMsg = "No such table,ensure one data with type of TData has been saved at least!";
return null;
}
return dataSet;
}
#region SQL Operation
/// <summary>
/// Create table where table name is type of TData
/// </summary>
/// <typeparam name="TData"></typeparam>
/// <returns></returns>
public string CreateTable<TData>() where TData: new() {
string result = "Success:";
try
{
string sqlCommand = "CREATE TABLE " + typeof(TData).Name;
sqlCommand += "(\"ID\" INTEGER NOT NULL UNIQUE,";
var properties = typeof(TData).GetFields();
foreach(var property in properties){
if(property.FieldType == typeof(int) || property.FieldType == typeof(bool) || property.FieldType.IsEnum){
sqlCommand += "\"" + property.Name + "\" INTEGER" + " NOT NULL,";
}else if(property.FieldType == typeof(double) || property.FieldType == typeof(float)){
sqlCommand += "\"" + property.Name + "\" REAL" + " NOT NULL,";
}
else{
sqlCommand += "\"" + property.Name + "\" TEXT" + " NOT NULL,";
}
}
sqlCommand += "PRIMARY KEY(\"ID\" AUTOINCREMENT))";
SqliteCommand createCommand = new SqliteCommand(sqlCommand,SqliteConn);
result = result + createCommand.ExecuteNonQuery().ToString();
}
catch (System.Exception e)
{
return "Error:"+e;
}
return result;
}
/// <summary>
/// Drop table where table name is type of TData
/// </summary>
/// <typeparam name="TData"></typeparam>
/// <returns></returns>
public string DropTable<TData>() where TData: new(){
string result = "Success:";
try
{
string sqlCommand = "DROP TABLE " + typeof(TData).Name;
SqliteCommand createCommand = new SqliteCommand(sqlCommand,SqliteConn);
result = result + createCommand.ExecuteNonQuery().ToString();
}
catch (System.Exception e)
{
return "Error:"+e;
}
return result;
}
/// <summary>
/// Insert data to table where table name is type of TData
/// </summary>
/// <param name="data"></param>
/// <typeparam name="TData"></typeparam>
/// <returns>success or error</returns>
public string InsertData<TData>(TData data) where TData: new(){
string result = "Success:";
try
{
string sqlCommand = "INSERT INTO " + typeof(TData).Name;
var properties = typeof(TData).GetFields();
Dictionary<string,object> dataParams = new Dictionary<string, object>();
foreach(var property in properties){
dataParams.Add(property.Name,property.GetValue(data));
if(property.FieldType==typeof(bool) || property.FieldType.IsEnum){
// If property is bool type , save data to data base should be 0 or 1 instead of false or true;
// If property is Enum type , then transform data to int;
dataParams[property.Name] = System.Convert.ToInt32(dataParams[property.Name]);
}else if(property.FieldType.IsClass || property.FieldType.IsValueType && !property.FieldType.IsPrimitive && property.FieldType != typeof(string)){
dataParams[property.Name] = JsonConvert.SerializeObject(dataParams[property.Name]);
}
}
sqlCommand += "(";
string keySet = "";
foreach(string key in dataParams.Keys){
keySet += key + ",";
}
keySet = keySet.TrimEnd(',');
sqlCommand += keySet;
sqlCommand += ") VALUES (";
string valueSet = "";
foreach(var value in dataParams.Values){
if(value.GetType() == typeof(int) || value.GetType() == typeof(float) || value.GetType() == typeof(double)){
valueSet += value + ",";
}else{
valueSet += "'" + value + "',";
}
}
valueSet = valueSet.TrimEnd(',');
sqlCommand += valueSet;
sqlCommand += ")";
SqliteCommand createCommand = new SqliteCommand(sqlCommand,SqliteConn);
result = result + createCommand.ExecuteNonQuery().ToString();
}
catch (System.Exception e)
{
ExceptionMsg = e.ToString();
return "Error:"+ExceptionMsg;
}
return result;
}
/// <summary>
/// Query Data and return object list with TData type,Support Data Type:ClassObject,Enum,int,string.float,struct.Not support double,if double then auto convert to float
/// </summary>
/// <returns>List of TData or null ,if null then you can print ExceptionMsg to check your error</returns>
public List<TData> SelectData<TData>() where TData: new(){
List<TData> resultList = new List<TData>();
try
{
string sqlCommand = "SELECT * FROM " + typeof(TData).Name;
SqliteCommand selectCommand = new SqliteCommand(sqlCommand,SqliteConn);
SqliteDataReader reader = selectCommand.ExecuteReader();
var properties = typeof(TData).GetFields();
while (reader.Read())
{
TData dataRow = new TData();
foreach(var property in properties){
if(property.FieldType == reader[property.Name].GetType()){
property.SetValue(dataRow,reader[property.Name]);
}else if(property.FieldType.IsEnum){
object propertyEnum = Enum.Parse(property.FieldType,reader[property.Name].ToString());
property.SetValue(dataRow,propertyEnum);
}
else if(property.FieldType.IsPrimitive) {
object propertyObject = System.Convert.ChangeType(reader[property.Name],property.FieldType);
property.SetValue(dataRow,propertyObject);
}else{
object classObject = JsonConvert.DeserializeObject(reader[property.Name].ToString(),property.FieldType);
property.SetValue(dataRow,classObject);
}
}
resultList.Add(dataRow);
}
}
catch (System.Exception e)
{
ExceptionMsg = e.ToString();
return null;
}
return resultList;
}
public bool IsTableExist<TData>() where TData:new(){
try
{
string sqlCommand = "SELECT name FROM sqlite_sequence";
SqliteCommand selectCommand = new SqliteCommand(sqlCommand,SqliteConn);
SqliteDataReader reader = selectCommand.ExecuteReader();
while (reader.Read()){
if(reader["name"].ToString()==typeof(TData).Name){
return true;
}
}
}
catch (System.Exception e)
{
ExceptionMsg = e.ToString();
return false;
}
return false;
}
#endregion
}
public static class CanGetEGSqliteExtension{
public static EGSqlite EGSqlite(this IEGFramework self){
return EGArchitectureImplement.Interface.GetModule<EGSqlite>();
}
}
}

122
addons/EGFramework/Module/SaveTools/EGSave.cs

@ -0,0 +1,122 @@ @@ -0,0 +1,122 @@
using System;
using System.IO;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace EGFramework
{
public interface IEGSave{
void SetDataToFile<TData>(TData data);
TData GetDataByFile<TData>() where TData : class,new();
void InitSaveData(string fileName);
}
public class EGSave : EGModule,IEGSave
{
private string DefaultSaveFile = "Default";
private string DefaultSaveFolder = "SaveData";
private JObject _SaveObject;
private JObject SaveObject{
get {
if(_SaveObject == null){
InitSaveObject();
}
return _SaveObject;
}
}
public EGSave(){
}
/// <summary>
/// if you want to define default save data file name, please use "this.RegisterModule(new EGSave("FileName"))"in your architecture code(Init function);
/// </summary>
/// <param name="fileName"></param>
public EGSave(string fileName){
this.DefaultSaveFile = fileName;
}
public override void Init()
{
if (!Directory.Exists(DefaultSaveFolder))
{
Directory.CreateDirectory(DefaultSaveFolder);
File.WriteAllText(DefaultSaveFolder + "/" + DefaultSaveFile + ".json","{}");
}else if(!File.Exists(DefaultSaveFolder + "/" + DefaultSaveFile + ".json")){
File.WriteAllText(DefaultSaveFolder + "/" + DefaultSaveFile + ".json","{}");
}
}
private void InitSaveObject(){
using (StreamReader reader = File.OpenText(DefaultSaveFolder + "/" + DefaultSaveFile + ".json"))
{
_SaveObject = (JObject)JToken.ReadFrom(new JsonTextReader(reader));
}
}
/// <summary>
/// Push SaveObject data set to file
/// </summary>
public void SaveToFile(){
SaveToFile(DefaultSaveFile);
}
private void SaveToFile(string fileName){
File.WriteAllText(DefaultSaveFolder + "/" + fileName + ".json",JsonConvert.SerializeObject(SaveObject,Formatting.Indented));
}
/// <summary>
/// Push data to SaveObject object cache, this function will not save data to file, if you hope not to IO operation frequently, you can use this with SaveToFile.
/// </summary>
/// <param name="data"></param>
/// <typeparam name="TData"></typeparam>
public void SetData<TData>(TData data){
//SaveObject = JObject.FromObject(data);
if(SaveObject.ContainsKey(typeof(TData).ToString())){
SaveObject[typeof(TData).ToString()] = JToken.FromObject(data);
}else{
SaveObject.Add(typeof(TData).ToString(),JToken.FromObject(data));
}
}
/// <summary>
/// Get data from file, if your data is not in file, then get null.
/// </summary>
/// <typeparam name="TData"></typeparam>
public TData GetDataByFile<TData>() where TData : class,new(){
if(!SaveObject.ContainsKey(typeof(TData).ToString())){
return null;
}
TData data = SaveObject[typeof(TData).ToString()].ToObject<TData>();
return data;
}
/// <summary>
/// Save data to file
/// </summary>
/// <param name="data">your any type of data</param>
/// <typeparam name="TData"></typeparam>
public void SetDataToFile<TData>(TData data)
{
SetData(data);
SaveToFile();
}
/// <summary>
/// Init a new save data file or load an other file with json suffix, if you want to load other save data, please use this function to reload;
/// </summary>
/// <param name="fileName"></param>
public void InitSaveData(string fileName)
{
DefaultSaveFile = fileName;
if(!File.Exists(DefaultSaveFolder + "/" + DefaultSaveFile + ".json")){
File.WriteAllText(DefaultSaveFolder + "/" + DefaultSaveFile + ".json","{}");
}
InitSaveObject();
}
}
public static class CanGetEGSaveExtension{
public static EGSave EGSave(this IEGFramework self){
return EGArchitectureImplement.Interface.GetModule<EGSave>();
}
}
}

230
addons/EGFramework/Module/WebDav/EGWebDav.cs

@ -0,0 +1,230 @@ @@ -0,0 +1,230 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Threading.Tasks;
using WebDav;
namespace EGFramework{
public class EGWebDav : IModule
{
public string ServerUrl { set; get; } = "";
private string UserName { set; get; } = "";
private string Password { set; get; } = "";
public bool IsInit { set; get; }
private WebDavClient WebDavClient { set; get; }
private string CurrentPath { set; get; } = "/";
public List<WebDavFileMsg> CurrentFileList { set; get; } = new List<WebDavFileMsg>();
public void Init()
{
}
public IArchitecture GetArchitecture()
{
return EGArchitectureImplement.Interface;
}
public void InitClient(string serverUrl, string userName,string password){
this.ServerUrl = serverUrl;
this.UserName = userName;
this.Password = password;
Dictionary<string,string> headersAdd = new Dictionary<string, string>
{
{ "Connection", "keep-alive" },
{ "Authorization", "Basic "+ EGWebDavExtension.EncodeCredentials(userName,password) }
};
WebDavClient = new WebDavClient(new WebDavClientParams
{
BaseAddress = new Uri(ServerUrl),
Credentials = new NetworkCredential(userName, password),
DefaultRequestHeaders = headersAdd
});
Console.WriteLine("Client has been init");
}
//---------download or upload from WebDav server---------//
/// <summary>
/// Download a file from dav path
/// </summary>
/// <param name="downloadUri">Such as /dav/Picture/Picture1.jpg</param>
/// <param name="localPath">download destination,such as C:\Users\W35\Pictures</param>
/// <param name="fileName">you can define file by this name,or by uri</param>
/// <returns></returns>
public async Task<bool> DownloadFile(string downloadUri,string localPath,string fileName = ""){
if (fileName.Equals("")){
fileName = Path.GetFileName(downloadUri);
}
using (var response = await WebDavClient.GetRawFile(downloadUri))
{
if(response.IsSuccessful == true){
// use response.Stream
using (FileStream DestinationStream = File.Create(localPath + "/" + fileName))
{
await response.Stream.CopyToAsync(DestinationStream);
//Print("【WebDav】" + fileName + "下载成功!");
}
return true;
}else{
return false;
}
}
}
public async Task<bool> DownloadFilProcessed(string downloadUri,string localPath,string fileName = ""){
if (fileName.Equals("")){
fileName = Path.GetFileName(downloadUri);
}
using (var response = await WebDavClient.GetProcessedFile(downloadUri))
{
if(response.IsSuccessful == true){
// use response.Stream
using (FileStream DestinationStream = File.Create(localPath + "/" + fileName))
{
await response.Stream.CopyToAsync(DestinationStream);
//Print("【WebDav】" + fileName + "下载成功!");
}
return true;
}else{
return false;
}
}
}
/// <summary>
/// Upload a file by localUrl
/// </summary>
/// <param name="localUrl">Such as C:\Users\W35\Pictures\Picture1.jpg</param>
/// <param name="uploadPath">upload destination,such as /dav/Picture</param>
/// <param name="fileName">you can define file by this name,or by local url</param>
/// <returns></returns>
public async Task<bool> UploadFile(string localUrl,string uploadPath,string fileName = ""){
if (fileName.Equals("")){
fileName = Path.GetFileName(localUrl);
}
// use response.Stream
var result = await WebDavClient.PutFile(uploadPath+"/"+fileName, File.OpenRead(localUrl));
if(result.IsSuccessful){
return true;
}else{
return false;
}
}
//-----------operate disk-----------//
/// <summary>
/// Default root path is "/",any path should be start with "/"
/// </summary>
/// <param name="currentPath"></param>
/// <returns></returns>
public async Task<List<WebDavFileMsg>> GetList(string currentPath){
PropfindResponse result = await WebDavClient.Propfind(ServerUrl+currentPath);
List<WebDavFileMsg> ResultFileList = new List<WebDavFileMsg>();
if (result.IsSuccessful)
{
foreach (WebDavResource res in result.Resources)
{
ResultFileList.Add(new WebDavFileMsg{
FileName = res.DisplayName ,
IsCollection = res.IsCollection ,
ContentLength = res.ContentLength ,
Uri = res.Uri ,
LastUpdateTime = res.LastModifiedDate
});
}
}
return ResultFileList;
}
/// <summary>
/// simple CD command, prop find all file message to CurrentFileList.
/// </summary>
/// <param name="destinationPath"></param>
/// <returns></returns>
public async Task ChangeDictionary(string destinationPath){
CurrentPath = destinationPath;
PropfindResponse result = await WebDavClient.Propfind(ServerUrl+CurrentPath);
CurrentFileList.Clear();
if (result.IsSuccessful)
{
foreach (WebDavResource res in result.Resources)
{
CurrentFileList.Add(new WebDavFileMsg{
FileName = res.DisplayName ,
IsCollection = res.IsCollection ,
ContentLength = res.ContentLength ,
Uri = res.Uri ,
LastUpdateTime = res.LastModifiedDate
});
}
}
}
/// <summary>
/// create a directory
/// </summary>
/// <param name="dictionaryName"></param>
/// <returns></returns>
public async Task MakeDictionary(string dictionaryName){
await WebDavClient.Mkcol(dictionaryName);
}
/// <summary>
/// simple cp command, copy a file with differentName.
/// </summary>
/// <param name="sourceFile"></param>
/// <param name="copyFile"></param>
/// <returns></returns>
public async Task Copy(string sourceFile,string copyFile){
await WebDavClient.Copy(sourceFile,copyFile);
}
/// <summary>
/// simple mv command, move a file with change fileName or different path.
/// </summary>
/// <param name="sourceFile"></param>
/// <param name="moveFile"></param>
/// <returns></returns>
public async Task Move(string sourceFile,string moveFile){
await WebDavClient.Move(sourceFile,moveFile);
}
/// <summary>
/// simple rm command,delete a file.
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
public async Task Remove(string fileName){
await WebDavClient.Delete(fileName);
}
}
public struct WebDavFileMsg{
public string FileName { set; get; }
public bool IsCollection { set; get; }
/// <summary>
/// unit is kb
/// </summary>
public long? ContentLength { set; get; }
public string Uri { set; get; }
public DateTime? LastUpdateTime { set; get; }
}
public static class EGWebDavExtension{
public static EGWebDav EGWebDav(this IEGFramework self)
{
return EGArchitectureImplement.Interface.GetModule<EGWebDav>();
}
public static string EncodeCredentials(string username, string password)
{
string credentials = $"{username}:{password}";
byte[] credentialsBytes = System.Text.Encoding.UTF8.GetBytes(credentials);
string encodedCredentials = Convert.ToBase64String(credentialsBytes);
return encodedCredentials;
}
}
}

1
icon.svg

@ -0,0 +1 @@ @@ -0,0 +1 @@
<svg height="128" width="128" xmlns="http://www.w3.org/2000/svg"><rect x="2" y="2" width="124" height="124" rx="14" fill="#363d52" stroke="#212532" stroke-width="4"/><g transform="scale(.101) translate(122 122)"><g fill="#fff"><path d="M105 673v33q407 354 814 0v-33z"/><path fill="#478cbf" d="m105 673 152 14q12 1 15 14l4 67 132 10 8-61q2-11 15-15h162q13 4 15 15l8 61 132-10 4-67q3-13 15-14l152-14V427q30-39 56-81-35-59-83-108-43 20-82 47-40-37-88-64 7-51 8-102-59-28-123-42-26 43-46 89-49-7-98 0-20-46-46-89-64 14-123 42 1 51 8 102-48 27-88 64-39-27-82-47-48 49-83 108 26 42 56 81zm0 33v39c0 276 813 276 813 0v-39l-134 12-5 69q-2 10-14 13l-162 11q-12 0-16-11l-10-65H447l-10 65q-4 11-16 11l-162-11q-12-3-14-13l-5-69z"/><path d="M483 600c3 34 55 34 58 0v-86c-3-34-55-34-58 0z"/><circle cx="725" cy="526" r="90"/><circle cx="299" cy="526" r="90"/></g><g fill="#414042"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></g></svg>

After

Width:  |  Height:  |  Size: 950 B

37
icon.svg.import

@ -0,0 +1,37 @@ @@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://31r216hix0wk"
path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://icon.svg"
dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

26
project.godot

@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
; Engine configuration file.
; It's best edited using the editor UI and not directly,
; since the parameters that go here are not all obvious.
;
; Format:
; [section] ; section goes between []
; param=value ; assign values to parameters
config_version=5
[application]
config/name="EGFramework"
config/tags=PackedStringArray("official")
run/main_scene="res://Example/Gateway/ModbusGateway.tscn"
config/features=PackedStringArray("4.2", "C#", "GL Compatibility")
config/icon="res://icon.svg"
[dotnet]
project/assembly_name="EGFramework"
[rendering]
renderer/rendering_method="gl_compatibility"
renderer/rendering_method.mobile="gl_compatibility"
Loading…
Cancel
Save