Compare commits

...
44 Commits
Author SHA1 Message Date
fengzeroz fed03422b3 Merge pull request #1212 from fengzeroz/v2.4
version: 2.4.3
2023-04-21 11:10:53 +08:00
fengzero c8a06bfc30 version: 2.4.3 2023-04-21 03:10:07 +00:00
eeff fc12d7fbff Merge pull request #1210 from eeff/eeff/2.4
Pick to 2.4 feat(mqtt): make offline cache setting more explicit
2023-04-20 18:09:29 +08:00
eeff 92f387ae7b feat(mqtt): make offline cache setting more explicit
1. Add `offline-cache` bool parameter.
  2. Require `cache-mem-size` and `cache-disk-size` only when
     `offline-cache` is enabled. We provide no defaults for
     `cache-mem-size` and `cache-disk-size`, so the user must provide
     values explicitly.
2023-04-20 16:43:45 +08:00
eeff 7cb61f6fa5 Merge pull request #1207 from eeff/eeff/2.4
feat: update global memory metrics
2023-04-20 13:48:19 +08:00
eeff 72eb9c4219 feat: update global memory metrics 2023-04-20 12:05:33 +08:00
hxy7yx faaef6c2e7 Open source Modbus TCP/RTU, delete existing Modbus TCP community 2023-04-20 11:15:06 +08:00
fengzeroz ff22e3413e Merge pull request #1204 from eeff/eeff/2.4
Pick to 2.4: make monitor schema description more clear
2023-04-20 09:55:15 +08:00
eeff 393d85741a chore: make monitor schema description more clear 2023-04-18 16:31:44 +08:00
fengzeroz 680a2839c2 Merge pull request #1201 from eeff/eeff/2.4
fix(restful): global config json parsing on empty array
2023-04-18 10:36:06 +08:00
eeff 5ee46fa972 fix(restful): global config json parsing on empty array 2023-04-18 10:14:19 +08:00
fengzeroz ae94644430 Merge pull request #1196 from fengzeroz/v2.4
version: 2.4.2
2023-04-14 15:39:59 +08:00
fengzero f5c730dcfc version: 2.4.2 2023-04-14 07:38:23 +00:00
eeff 488cccf521 Merge pull request #1185 from eeff/eeff/2.4
Pick to 2.4: data race when close mqtt client
2023-04-12 17:43:19 +08:00
eeff a0d5851719 fix: data race when close mqtt client 2023-04-12 11:10:58 +08:00
eeff c518d6c196 Merge pull request #1181 from eeff/eeff/2.4
Pick to 2.4: sql schema add default setting for data-strream-processing node
2023-04-11 16:22:20 +08:00
eeff c385103c9c feat: sql schema add default setting for data-strream-processing node 2023-04-11 16:03:47 +08:00
eeff f216b3f4e9 Merge pull request #1180 from eeff/eeff/2.4
Pick to 2.4: update mqtt schema
2023-04-11 12:14:56 +08:00
eeff c768f45e9e chore: update mqtt schema 2023-04-10 17:29:45 +08:00
eeff fa55497b55 chore: make mqtt configuration parameter description more clear 2023-04-10 17:29:41 +08:00
fengzeroz 5336d1c250 Merge pull request #1178 from fengzeroz/v2.4
improve(connection): replace old client
2023-04-10 14:08:35 +08:00
fengzero dede8ee8f4 fix(connection): replace client 2023-04-10 03:33:54 +00:00
fengzero 17e2015a9c fix compile warning 2023-04-10 03:26:17 +00:00
fengzero 3361284252 improve(connection): replace old client 2023-04-10 03:12:12 +00:00
fengzeroz 8f6d9d1c41 Merge pull request #1174 from fengzeroz/v2.4
version: 2.4.1
2023-04-07 16:52:38 +08:00
fengzero 3f75222410 version: 2.4.1 2023-04-07 08:52:01 +00:00
fengzeroz ddf5d252f6 Merge pull request #1173 from ZhouBox/feature-build-2.4
[v2.4]feature:use ghcr.io and use corresponding tag
2023-04-07 16:06:42 +08:00
zhouxiang 6e467f5f27 fix:use tag directly 2023-04-07 15:41:36 +08:00
zhouxiang fd1266bc9e [v2.4]feature:use ghcr.io and use corresponding tag 2023-04-07 14:39:44 +08:00
fengzeroz d8b605ef1c Merge pull request #1172 from fengzeroz/v2.4
serial: more baudrate 300/200/150
2023-04-07 14:23:16 +08:00
fengzero c9122b5b7c serial: more baudrate 300/200/150 2023-04-07 06:21:52 +00:00
fengzeroz 8c11cb3466 Merge pull request #1170 from gc87/spb-float-fix-v2.4
[v2.4]fix: determined f32 type data is treated as d64
2023-04-07 11:07:41 +08:00
gc87 80680c55f5 fix: determined f32 type data is treated as d64 2023-04-07 10:51:14 +08:00
fengzero 132c25b4ad serial: more baudrate 1800/1200/600 2023-04-06 08:14:38 +00:00
fengzeroz cc7e5d092a Merge pull request #1162 from lixiumei123/v2.4
fix(log debug):error reporting that the node does not exist
2023-04-06 14:38:56 +08:00
liyuxi 2e3f6d6db2 fix(log debug):error reporting that the node does not exist 2023-04-06 06:20:56 +00:00
fengzeroz 43a95b7910 Merge pull request #1156 from eeff/eeff/2.4
Pick to 2.4: schema use drop drown selection button for QoS setting
2023-04-06 13:01:09 +08:00
fengzeroz 053be086b0 Merge pull request #1158 from lixiumei123/v2.4
fix(file plugin):fix delay
2023-04-06 13:00:26 +08:00
fengzeroz 66dba0f69a Merge pull request #1160 from fengzeroz/v2.4
limit log size
2023-04-06 12:59:12 +08:00
fengzero c6752c5761 limit log size 2023-04-06 04:57:46 +00:00
liyuxi 53f5c496ea fix(file plugin):fix delay 2023-04-06 02:26:46 +00:00
eeff 1e28e4bc0e fix(mqtt): schema use drop drown selection button for QoS setting 2023-04-04 19:03:41 +08:00
fengzeroz 2bbcc6a154 Merge pull request #1154 from fengzeroz/v2.4
ci: branch v2.4
2023-03-31 17:34:16 +08:00
fengzero 00d6418288 ci: branch v2.4 2023-03-31 09:33:43 +00:00
47 changed files with 2053 additions and 486 deletions
-1
View File
@@ -12,4 +12,3 @@ PointerAlignment: Right
SpaceAfterCStyleCast: true
AllowShortFunctionsOnASingleLine: Inline
BreakBeforeBinaryOperators: None
+7 -2
View File
@@ -7,6 +7,11 @@ on:
types:
- published
env:
BRANCH: v2.4
jobs:
clang_format_check:
runs-on: ubuntu-20.04
@@ -40,7 +45,7 @@ jobs:
ut:
runs-on: ubuntu-20.04
container: neugates/build:x86_64-main
container: ghcr.io/neugates/build:x86_64-v2.4
steps:
- uses: actions/checkout@v2
@@ -61,7 +66,7 @@ jobs:
ft:
runs-on: ubuntu-20.04
container: neugates/build:x86_64-main
container: ghcr.io/neugates/build:x86_64-v2.4
steps:
- uses: actions/checkout@v2
+5 -5
View File
@@ -8,9 +8,9 @@ on:
- published
env:
VERSION: 2.4.0
UI_VERSION: 2.4.0
BRANCH: main
VERSION: 2.4.3
UI_VERSION: 2.4.2
BRANCH: v2.4
jobs:
cross-build:
@@ -21,7 +21,7 @@ jobs:
- [aarch64-linux-gnu, aarch64, arm64]
- [arm-linux-gnueabihf, armv4, armhf]
- [x86_64-linux-gnu, x86_64, amd64]
container: neugates/build:${{ matrix.arch[1] }}-main
container: ghcr.io/neugates/build:${{ matrix.arch[1] }}-v2.4
steps:
- uses: actions/checkout@v3
@@ -64,7 +64,7 @@ jobs:
matrix:
arch:
- [x86_64-linux-gnu, x86_64, amd64]
container: neugates/build:${{ matrix.arch[1] }}-main
container: ghcr.io/neugates/build:${{ matrix.arch[1] }}-v2.4
steps:
- uses: actions/checkout@v3
+3 -1
View File
@@ -3,7 +3,9 @@
"libplugin-mqtt.so",
"libplugin-ekuiper.so",
"libplugin-monitor.so",
"libplugin-modbus-tcp-comm.so",
"libplugin-modbus-tcp.so",
"libplugin-modbus-rtu.so",
"libplugin-modbus-qh-tcp.so",
"libplugin-file.so"
]
}
+1 -1
View File
@@ -9,5 +9,5 @@ file_format = "%d [%V] %f:%L %m%n"
[rules]
*.* "./logs/%c.log", 100MB * 10 ~ "./logs/%c.#2s.log"; file_format
*.* "./logs/%c.log", 50MB * 1 ~ "./logs/%c.#2s.log"; file_format
*.* >stdout; stdout_format
+184
View File
@@ -0,0 +1,184 @@
*** Settings ***
Resource resource/api.resource
Resource resource/common.resource
Resource resource/error.resource
Resource resource/driver.resource
Suite Setup Modbus Test Setup
Suite Teardown Modbus Test Teardown
*** Variables ***
${modbus_rtu_node} modbus_rtu_node
${PLUGIN-MODBUS-RTU} Modbus RTU
${MODBUS_RTU_CONFIG} {"link": 1, "timeout": 3000, "interval": 10, "device": "", "stop": 0, "parity": 0, "baud": 4,"data": 3, "host": "127.0.0.1", "port": 60503, "connection_mode": 0}
${MODBUS_RTU_CONFIG_WRANING} {"link": 1, "host": "127.0.0.1","port": 60503,"connection_mode": 0}
${hold_bit} {"name": "hold_bit", "address": "1!400001.15", "attribute": ${TAG_ATTRIBUTE_READ}, "type": ${TAG_TYPE_BIT}}
${hold_int16} {"name": "hold_int16", "address": "1!400001", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_TYPE_INT16}}
${hold_uint16} {"name": "hold_uint16", "address": "1!400002", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_TYPE_UINT16}}
${hold_int32} {"name": "hold_int32", "address": "1!400003", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_TYPE_INT32}}
${hold_uint32} {"name": "hold_uint32", "address": "1!400015", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_TYPE_UINT32}}
${hold_float} {"name": "hold_float", "address": "1!400017", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_TYPE_FLOAT}}
${hold_string} {"name": "hold_string", "address": "1!400020.10", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_TYPE_STRING}}
${coil_bit_1} {"name": "coil_bit_1", "address": "1!000001", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_TYPE_BIT}}
${coil_bit_2} {"name": "coil_bit_2", "address": "1!000002", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_TYPE_BIT}}
${coil_bit_3} {"name": "coil_bit_3", "address": "1!000003", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_TYPE_BIT}}
${coil_bit_4} {"name": "coil_bit_4", "address": "1!000005", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_TYPE_BIT}}
${coil_bit_5} {"name": "coil_bit_5", "address": "1!000010", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_TYPE_BIT}}
${input_bit_1} {"name": "input_bit_1", "address": "1!100001", "attribute": ${TAG_ATTRIBUTE_READ}, "type": ${TAG_TYPE_BIT}}
${input_bit_2} {"name": "input_bit_2", "address": "1!100002", "attribute": ${TAG_ATTRIBUTE_READ}, "type": ${TAG_TYPE_BIT}}
${input_bit_3} {"name": "input_bit_3", "address": "1!100003", "attribute": ${TAG_ATTRIBUTE_READ}, "type": ${TAG_TYPE_BIT}}
${input_bit_4} {"name": "input_bit_4", "address": "1!100005", "attribute": ${TAG_ATTRIBUTE_READ}, "type": ${TAG_TYPE_BIT}}
${input_bit_5} {"name": "input_bit_5", "address": "1!100010", "attribute": ${TAG_ATTRIBUTE_READ}, "type": ${TAG_TYPE_BIT}}
${input_register_bit} {"name": "input_register_bit", "address": "1!30101.15", "attribute": ${TAG_ATTRIBUTE_READ}, "type": ${TAG_TYPE_BIT}}
${input_register_int16} {"name": "input_register_int16", "address": "1!30101", "attribute": ${TAG_ATTRIBUTE_READ}, "type": ${TAG_TYPE_INT16}}
${input_register_uint16} {"name": "input_register_uint16", "address": "1!30102", "attribute": ${TAG_ATTRIBUTE_READ}, "type": ${TAG_TYPE_UINT16}}
${input_register_int32} {"name": "input_register_int32", "address": "1!30103", "attribute": ${TAG_ATTRIBUTE_READ}, "type": ${TAG_TYPE_INT32}}
${input_register_uint32} {"name": "input_register_uint32", "address": "1!30115", "attribute": ${TAG_ATTRIBUTE_READ}, "type": ${TAG_TYPE_UINT32}}
${input_register_float} {"name": "input_register_float", "address": "1!30117", "attribute": ${TAG_ATTRIBUTE_READ}, "type": ${TAG_TYPE_FLOAT}}
${input_register_string} {"name": "input_register_string", "address": "1!30120.10", "attribute": ${TAG_ATTRIBUTE_READ}, "type": ${TAG_TYPE_STRING}}
${hold_int16_address_update} {"name": "hold_int16", "address": "1!400100", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_TYPE_INT16}}
${hold_int16_type_update} {"name": "hold_int16", "address": "1!400001", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_TYPE_INT32}}
${input_bit_1_name_wrong} {"name": "input_bit", "address": "1!100001", "attribute": ${TAG_ATTRIBUTE_READ}, "type": ${TAG_TYPE_BIT}}
${input_bit_1_attribute_unmatch} {"name": "input_bit_1", "address": "1!100001", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_TYPE_BIT}}
${input_bit_1_type_unmatch} {"name": "input_bit_1", "address": "1!100001", "attribute": ${TAG_ATTRIBUTE_READ}, "type": ${TAG_TYPE_INT16}}
*** Test Cases ***
Set a node with right settings, it should be success.
[Template] Set a ${node} with right ${node_settings}, it will be success.
${modbus_rtu_node} ${MODBUS_RTU_CONFIG}
Set a node with wrong settings, it should be failure.
[Template] Set a ${node} with wrong ${node_settings}, it will be failure.
${modbus_rtu_node} ${MODBUS_RTU_CONFIG_WRANING}
Set tags with multiple address and types and attribute, it should be success.
[Template] Add a ${tag} with right address and type and attribute from ${node}, it will be success.
${hold_int16} ${modbus_rtu_node}
${hold_uint16} ${modbus_rtu_node}
${hold_int32} ${modbus_rtu_node}
${hold_uint32} ${modbus_rtu_node}
${hold_float} ${modbus_rtu_node}
${hold_string} ${modbus_rtu_node}
${coil_bit_1} ${modbus_rtu_node}
${coil_bit_2} ${modbus_rtu_node}
${coil_bit_3} ${modbus_rtu_node}
${coil_bit_4} ${modbus_rtu_node}
${coil_bit_5} ${modbus_rtu_node}
${input_register_int16} ${modbus_rtu_node}
${input_register_uint16} ${modbus_rtu_node}
${input_register_int32} ${modbus_rtu_node}
${input_register_uint32} ${modbus_rtu_node}
${input_register_float} ${modbus_rtu_node}
${input_register_string} ${modbus_rtu_node}
${input_bit_1} ${modbus_rtu_node}
${input_bit_2} ${modbus_rtu_node}
${input_bit_3} ${modbus_rtu_node}
${input_bit_4} ${modbus_rtu_node}
${input_bit_5} ${modbus_rtu_node}
Set tags with unmatch address, type or attribute, it should be failure.
[Template] Set a ${tag} with unmatch address, type or attribute from ${node}, it will be failure and return ${resp_status} and ${error_code}.
${input_bit_1_type_unmatch} ${modbus_rtu_node} 200 ${NEU_ERR_TAG_TYPE_NOT_SUPPORT}
${input_bit_1_attribute_unmatch} ${modbus_rtu_node} 200 ${NEU_ERR_TAG_ATTRIBUTE_NOT_SUPPORT}
Write and Read all type tag with same slave id in hold register and coil, it should be success.
[Template] Write and Read a ${tag} named ${tag_name} from ${node}, using ${check} to check the ${value}, it will be success.
${hold_int16} hold_int16 ${modbus_rtu_node} Compare Tag Value As Int 120
${hold_uint16} hold_uint16 ${modbus_rtu_node} Compare Tag Value As Int 65535
${hold_int32} hold_int32 ${modbus_rtu_node} Compare Tag Value As Int 77889912
${hold_uint32} hold_uint32 ${modbus_rtu_node} Compare Tag Value As Int 88991232
${hold_float} hold_float ${modbus_rtu_node} Compare Tag Value As Float 12.34
${hold_string} hold_string ${modbus_rtu_node} Compare Tag Value As Strings "abc123"
${coil_bit_1} coil_bit_1 ${modbus_rtu_node} Compare Tag Value As Int 1
${coil_bit_2} coil_bit_2 ${modbus_rtu_node} Compare Tag Value As Int 0
${coil_bit_3} coil_bit_3 ${modbus_rtu_node} Compare Tag Value As Int 0
${coil_bit_4} coil_bit_4 ${modbus_rtu_node} Compare Tag Value As Int 1
${coil_bit_5} coil_bit_5 ${modbus_rtu_node} Compare Tag Value As Int 1
Read all type tag with same slave id in input register and input, it should be success.
[Template] Read a ${tag} named ${tag_name} from ${node}, using ${check} to check the ${value}, it will be success.
${input_register_int16} input_register_int16 ${modbus_rtu_node} Compare Tag Value As Int 0
${input_register_uint16} input_register_uint16 ${modbus_rtu_node} Compare Tag Value As Int 0
${input_register_int32} input_register_int32 ${modbus_rtu_node} Compare Tag Value As Int 0
${input_register_uint32} input_register_uint32 ${modbus_rtu_node} Compare Tag Value As Int 0
${input_register_float} input_register_float ${modbus_rtu_node} Compare Tag Value As Float 0
${input_register_string} input_register_string ${modbus_rtu_node} Compare Tag Value As Strings ""
${input_bit_1} input_bit_1 ${modbus_rtu_node} Compare Tag Value As Int 0
${input_bit_2} input_bit_2 ${modbus_rtu_node} Compare Tag Value As Int 0
${input_bit_3} input_bit_3 ${modbus_rtu_node} Compare Tag Value As Int 0
${input_bit_4} input_bit_4 ${modbus_rtu_node} Compare Tag Value As Int 0
${input_bit_5} input_bit_5 ${modbus_rtu_node} Compare Tag Value As Int 0
Read some tags at time, it will be success.
[Template] Read some ${tags} from ${node}, it will be success.
${input_register_int16},${input_register_uint16},${input_register_int32},${input_register_uint32},${input_register_float},${input_register_string},${input_register_bit} ${modbus_rtu_node}
${hold_int16},${hold_uint16},${hold_int32},${hold_uint32},${hold_float},${hold_string},${hold_bit} ${modbus_rtu_node}
${coil_bit_1},${coil_bit_2},${coil_bit_3},${coil_bit_4},${coil_bit_5} ${modbus_rtu_node}
${input_bit_1},${input_bit_2},${input_bit_3},${input_bit_4},${input_bit_5} ${modbus_rtu_node}
Write and read a tag with same name and same address that in different groups, it should be success.
${groups}= Create List group-1 group-2 group-3
Write and read a ${hold_int16} with same hold_int16 and same address in different ${groups} that from ${modbus_rtu_node}, using Compare Tag Value As Int to check the 2, it will be success.
Write and read a tag with same name but different address that in different groups, it should be success.
${groups}= Create List group-1 group-2 group-3
Write and read ${hold_int16} ${hold_int16_address_update} with same hold_int16 but different address in different ${groups} that from ${modbus_rtu_node}, using Compare Tag Value As Int to check the 2, it will be success.
Update a tag with right address and types and attribute, it will be success
[Template] Update a ${tag} by a ${tag_update} named ${tag_name} from ${node} , it can read and write, using ${check} to check the ${value}, it will be success.
${hold_int16} ${hold_int16_address_update} hold_int16 ${modbus_rtu_node} Compare Tag Value As Int 120
${hold_int16} ${hold_int16_type_update} hold_int16 ${modbus_rtu_node} Compare Tag Value As Int 120
Update a tag with unmatch address and types and attribute, it should be failure.
[Template] Update a ${tag} by a ${tag_update} with the unmatch settings from ${node} , it will be failure and return ${resp_status} and ${error_code}.
${input_bit_1} ${input_bit_1_attribute_unmatch} ${modbus_rtu_node} 200 ${NEU_ERR_TAG_ATTRIBUTE_NOT_SUPPORT}
${input_bit_1} ${input_bit_1_type_unmatch} ${modbus_rtu_node} 200 ${NEU_ERR_TAG_TYPE_NOT_SUPPORT}
${input_bit_1} ${input_bit_1_name_wrong} ${modbus_rtu_node} 200 ${NEU_ERR_TAG_NOT_EXIST}
Write a value to a tag that don't exit in plc or neuron, it will be failure.
[Template] Write a ${value} to a ${tag} with named ${tag_name} that don't exist in neuron or plc from ${node}, it will be failure.
12 ${input_bit_1} input ${modbus_rtu_node}
Write value to a tag without write attribute, it will be failure.
[Template] Write a ${value} to a ${tag} named ${tag_name} that without write attribute from ${node}, it will be failure.
1 ${input_bit_1} input_bit_1 ${modbus_rtu_node}
Write and read a value to a tag when the node is stoped, it will be failure.
[Template] Write and read a ${value} to a ${tag} named ${tag_name} when the ${node} is stoped, it will be failure.
1 ${hold_int16} hold_int16 ${modbus_rtu_node}
*** Keywords ***
Modbus Test Setup
Start Modbus-RTU Simulator
Start Neuronx
${res}= Add Node ${modbus_rtu_node} ${PLUGIN-MODBUS-RTU}
Check Response Status ${res} 200
${res}= Node Setting ${modbus_rtu_node} ${MODBUS_RTU_CONFIG}
Check Response Status ${res} 200
${state}= Get Node State ${modbus_rtu_node}
Should Be Equal As Integers ${state}[running] ${NODE_STATE_RUNNING}
Should Be Equal As Integers ${state}[link] ${NODE_LINK_STATE_DISCONNECTED}
Modbus Test Teardown
Del Node ${modbus_rtu_node}
Stop Neuronx
Terminate All Processes kill=false
Start Modbus-RTU Simulator
${handle} = Start Process ${MODBUS_TCP_SERVER_SIMULATOR} rtu 60503 cwd=build/simulator
[Return] ${handle}
+77 -90
View File
@@ -1,4 +1,7 @@
*** Settings ***
Library Process
Library OperatingSystem
Resource resource/api.resource
Resource resource/common.resource
Resource resource/error.resource
@@ -7,9 +10,13 @@ Suite Setup Modbus Test Setup
Suite Teardown Modbus Test Teardown
*** Variables ***
${modbus_node} modbus_node
${PLUGIN-MODBUS-TCP} Modbus TCP
${MODBUS_TCP_CONFIG_WRONG} {"host": "127.0.0.1", "port": 60502}
${modbus_tcp_node} modbus_tcp_node
${MODBUS_TCP_CONFIG} {"connection_mode": 0,"interval": 0, "host": "127.0.0.1","port": 60502,"timeout": 3000}
${MODBUS_TCP_CONFIG_WRONG} {"connection_mode": 0,"host": "127.0.0.1", "port": 60502}
${hold_bit} {"name": "hold_bit", "address": "1!400001.15", "attribute": ${TAG_ATTRIBUTE_READ}, "type": ${TAG_TYPE_BIT}}
${hold_int16} {"name": "hold_int16", "address": "1!400001", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_TYPE_INT16}}
@@ -46,135 +53,115 @@ ${input_bit_1_name_wrong} {"name": "input_bit", "address": "1!100001", "att
${input_bit_1_attribute_unmatch} {"name": "input_bit_1", "address": "1!100001", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_TYPE_BIT}}
${input_bit_1_type_unmatch} {"name": "input_bit_1", "address": "1!100001", "attribute": ${TAG_ATTRIBUTE_READ}, "type": ${TAG_TYPE_INT16}}
${hold_int16_static} {"name": "hold_int16", "address": "1!400001", "attribute": ${TAG_ATTRIBUTE_STATIC_RW}, "type": ${TAG_TYPE_INT16}, "value": 1}
*** Test Cases ***
Set a node with right settings, it should be success.
[Template] Set a ${node} with right ${node_settings}, it will be success.
${modbus_node} ${MODBUS_TCP_CONFIG}
${modbus_tcp_node} ${MODBUS_TCP_CONFIG}
Set a node with wrong settings, it should be failure.
[Template] Set a ${node} with wrong ${node_settings}, it will be failure.
${modbus_node} ${MODBUS_TCP_CONFIG_WRONG}
${modbus_tcp_node} ${MODBUS_TCP_CONFIG_WRONG}
Set tags with multiple address and types and attribute, it should be success.
[Template] Add a ${tag} with right address and type and attribute from ${node}, it will be success.
${hold_int16} ${modbus_node}
${hold_uint16} ${modbus_node}
${hold_int32} ${modbus_node}
${hold_uint32} ${modbus_node}
${hold_float} ${modbus_node}
${hold_string} ${modbus_node}
${hold_int16} ${modbus_tcp_node}
${hold_uint16} ${modbus_tcp_node}
${hold_int32} ${modbus_tcp_node}
${hold_uint32} ${modbus_tcp_node}
${hold_float} ${modbus_tcp_node}
${hold_string} ${modbus_tcp_node}
${coil_bit_1} ${modbus_node}
${coil_bit_2} ${modbus_node}
${coil_bit_3} ${modbus_node}
${coil_bit_4} ${modbus_node}
${coil_bit_5} ${modbus_node}
${coil_bit_1} ${modbus_tcp_node}
${coil_bit_2} ${modbus_tcp_node}
${coil_bit_3} ${modbus_tcp_node}
${coil_bit_4} ${modbus_tcp_node}
${coil_bit_5} ${modbus_tcp_node}
${input_register_int16} ${modbus_node}
${input_register_uint16} ${modbus_node}
${input_register_int32} ${modbus_node}
${input_register_uint32} ${modbus_node}
${input_register_float} ${modbus_node}
${input_register_string} ${modbus_node}
${input_register_int16} ${modbus_tcp_node}
${input_register_uint16} ${modbus_tcp_node}
${input_register_int32} ${modbus_tcp_node}
${input_register_uint32} ${modbus_tcp_node}
${input_register_float} ${modbus_tcp_node}
${input_register_string} ${modbus_tcp_node}
${input_bit_1} ${modbus_node}
${input_bit_2} ${modbus_node}
${input_bit_3} ${modbus_node}
${input_bit_4} ${modbus_node}
${input_bit_5} ${modbus_node}
${input_bit_1} ${modbus_tcp_node}
${input_bit_2} ${modbus_tcp_node}
${input_bit_3} ${modbus_tcp_node}
${input_bit_4} ${modbus_tcp_node}
${input_bit_5} ${modbus_tcp_node}
Set tags with unmatch address, type or attribute, it should be failure.
[Template] Set a ${tag} with unmatch address, type or attribute from ${node}, it will be failure and return ${resp_status} and ${error_code}.
${input_bit_1_type_unmatch} ${modbus_node} 200 ${NEU_ERR_TAG_TYPE_NOT_SUPPORT}
${input_bit_1_attribute_unmatch} ${modbus_node} 200 ${NEU_ERR_TAG_ATTRIBUTE_NOT_SUPPORT}
${input_bit_1_type_unmatch} ${modbus_tcp_node} 200 ${NEU_ERR_TAG_TYPE_NOT_SUPPORT}
${input_bit_1_attribute_unmatch} ${modbus_tcp_node} 200 ${NEU_ERR_TAG_ATTRIBUTE_NOT_SUPPORT}
Write and Read all type tag with same slave id in hold register and coil, it should be success.
[Template] Write and Read a ${tag} named ${tag_name} from ${node}, using ${check} to check the ${value}, it will be success.
${hold_int16} hold_int16 ${modbus_node} Compare Tag Value As Int 120
${hold_uint16} hold_uint16 ${modbus_node} Compare Tag Value As Int 65535
${hold_int32} hold_int32 ${modbus_node} Compare Tag Value As Int 77889912
${hold_uint32} hold_uint32 ${modbus_node} Compare Tag Value As Int 88991232
${hold_float} hold_float ${modbus_node} Compare Tag Value As Float 12.34
${hold_string} hold_string ${modbus_node} Compare Tag Value As Strings "abc123"
${hold_int16} hold_int16 ${modbus_tcp_node} Compare Tag Value As Int 120
${hold_uint16} hold_uint16 ${modbus_tcp_node} Compare Tag Value As Int 65535
${hold_int32} hold_int32 ${modbus_tcp_node} Compare Tag Value As Int 77889912
${hold_uint32} hold_uint32 ${modbus_tcp_node} Compare Tag Value As Int 88991232
${hold_float} hold_float ${modbus_tcp_node} Compare Tag Value As Float 12.34
${hold_string} hold_string ${modbus_tcp_node} Compare Tag Value As Strings "abc123"
${coil_bit_1} coil_bit_1 ${modbus_node} Compare Tag Value As Int 1
${coil_bit_2} coil_bit_2 ${modbus_node} Compare Tag Value As Int 0
${coil_bit_3} coil_bit_3 ${modbus_node} Compare Tag Value As Int 0
${coil_bit_4} coil_bit_4 ${modbus_node} Compare Tag Value As Int 1
${coil_bit_5} coil_bit_5 ${modbus_node} Compare Tag Value As Int 1
${coil_bit_1} coil_bit_1 ${modbus_tcp_node} Compare Tag Value As Int 1
${coil_bit_2} coil_bit_2 ${modbus_tcp_node} Compare Tag Value As Int 0
${coil_bit_3} coil_bit_3 ${modbus_tcp_node} Compare Tag Value As Int 0
${coil_bit_4} coil_bit_4 ${modbus_tcp_node} Compare Tag Value As Int 1
${coil_bit_5} coil_bit_5 ${modbus_tcp_node} Compare Tag Value As Int 1
Read all type tag with same slave id in input register and input, it should be success.
[Template] Read a ${tag} named ${tag_name} from ${node}, using ${check} to check the ${value}, it will be success.
${input_register_int16} input_register_int16 ${modbus_node} Compare Tag Value As Int 0
${input_register_uint16} input_register_uint16 ${modbus_node} Compare Tag Value As Int 0
${input_register_int32} input_register_int32 ${modbus_node} Compare Tag Value As Int 0
${input_register_uint32} input_register_uint32 ${modbus_node} Compare Tag Value As Int 0
${input_register_float} input_register_float ${modbus_node} Compare Tag Value As Float 0
${input_register_string} input_register_string ${modbus_node} Compare Tag Value As Strings ""
${input_register_int16} input_register_int16 ${modbus_tcp_node} Compare Tag Value As Int 0
${input_register_uint16} input_register_uint16 ${modbus_tcp_node} Compare Tag Value As Int 0
${input_register_int32} input_register_int32 ${modbus_tcp_node} Compare Tag Value As Int 0
${input_register_uint32} input_register_uint32 ${modbus_tcp_node} Compare Tag Value As Int 0
${input_register_float} input_register_float ${modbus_tcp_node} Compare Tag Value As Float 0
${input_register_string} input_register_string ${modbus_tcp_node} Compare Tag Value As Strings ""
${input_bit_1} input_bit_1 ${modbus_node} Compare Tag Value As Int 0
${input_bit_2} input_bit_2 ${modbus_node} Compare Tag Value As Int 0
${input_bit_3} input_bit_3 ${modbus_node} Compare Tag Value As Int 0
${input_bit_4} input_bit_4 ${modbus_node} Compare Tag Value As Int 0
${input_bit_5} input_bit_5 ${modbus_node} Compare Tag Value As Int 0
${input_bit_1} input_bit_1 ${modbus_tcp_node} Compare Tag Value As Int 0
${input_bit_2} input_bit_2 ${modbus_tcp_node} Compare Tag Value As Int 0
${input_bit_3} input_bit_3 ${modbus_tcp_node} Compare Tag Value As Int 0
${input_bit_4} input_bit_4 ${modbus_tcp_node} Compare Tag Value As Int 0
${input_bit_5} input_bit_5 ${modbus_tcp_node} Compare Tag Value As Int 0
Read some tags at time, it will be success.
${input_register_tags}= Create List ${input_register_int16} ${input_register_uint16} ${input_register_int32} ${input_register_uint32} ${input_register_float} ${input_register_string} ${input_register_bit}
Read some ${input_register_tags} from ${modbus_node}, it will be success.
${hold_tags}= Create List ${hold_int16} ${hold_uint16} ${hold_int32} ${hold_uint32} ${hold_float} ${hold_string} ${hold_bit}
Read some ${hold_tags} from ${modbus_node}, it will be success.
${coil_tags}= Create List ${coil_bit_1} ${coil_bit_2} ${coil_bit_3} ${coil_bit_4} ${coil_bit_5}
Read some ${coil_tags} from ${modbus_node}, it will be success.
${input_tags}= Create List ${input_bit_1} ${input_bit_2} ${input_bit_3} ${input_bit_4} ${input_bit_5}
Read some ${input_tags} from ${modbus_node}, it will be success.
Read static tag, it should be success.
[Template] Read a ${tag} named ${tag_name} from ${node}, using ${check} to check the ${value}, it will be success.
${hold_int16_static} hold_int16 ${modbus_node} Compare Tag Value As Int 1
Write a static tag, it should be success.
[Template] Write and Read a ${tag} named ${tag_name} from ${node}, using ${check} to check the ${value}, it will be success.
${hold_int16_static} hold_int16 ${modbus_node} Compare Tag Value As Int 2
[Template] Read some ${tags} from ${node}, it will be success.
${input_register_int16},${input_register_uint16},${input_register_int32},${input_register_uint32},${input_register_float},${input_register_string},${input_register_bit} ${modbus_tcp_node}
${hold_int16},${hold_uint16},${hold_int32},${hold_uint32},${hold_float},${hold_string},${hold_bit} ${modbus_tcp_node}
${coil_bit_1},${coil_bit_2},${coil_bit_3},${coil_bit_4},${coil_bit_5} ${modbus_tcp_node}
${input_bit_1},${input_bit_2},${input_bit_3},${input_bit_4},${input_bit_5} ${modbus_tcp_node}
Write and read a tag with same name and same address that in different groups, it should be success.
${groups}= Create List group-1 group-2 group-3
Write and read a ${hold_int16} with same hold_int16 and same address in different ${groups} that from ${modbus_node}, using Compare Tag Value As Int to check the 2, it will be success.
Write and read a ${hold_int16} with same hold_int16 and same address in different ${groups} that from ${modbus_tcp_node}, using Compare Tag Value As Int to check the 2, it will be success.
Write and read a tag with same name but different address that in different groups, it should be success.
${groups}= Create List group-1 group-2 group-3
Write and read ${hold_int16} ${hold_int16_address_update} with same hold_int16 but different address in different ${groups} that from ${modbus_node}, using Compare Tag Value As Int to check the 2, it will be success.
Write and read ${hold_int16} ${hold_int16_address_update} with same hold_int16 but different address in different ${groups} that from ${modbus_tcp_node}, using Compare Tag Value As Int to check the 2, it will be success.
Update a tag with right address and types and attribute, it will be success
[Template] Update a ${tag} by a ${tag_update} named ${tag_name} from ${node} , it can read and write, using ${check} to check the ${value}, it will be success.
${hold_int16} ${hold_int16_address_update} hold_int16 ${modbus_node} Compare Tag Value As Int 120
${hold_int16} ${hold_int16_type_update} hold_int16 ${modbus_node} Compare Tag Value As Int 120
${hold_int16} ${hold_int16_address_update} hold_int16 ${modbus_tcp_node} Compare Tag Value As Int 120
${hold_int16} ${hold_int16_type_update} hold_int16 ${modbus_tcp_node} Compare Tag Value As Int 120
Update a tag with unmatch address and types and attribute, it should be failure.
[Template] Update a ${tag} by a ${tag_update} with the unmatch settings from ${node} , it will be failure and return ${resp_status} and ${error_code}.
${input_bit_1} ${input_bit_1_attribute_unmatch} ${modbus_node} 200 ${NEU_ERR_TAG_ATTRIBUTE_NOT_SUPPORT}
${input_bit_1} ${input_bit_1_type_unmatch} ${modbus_node} 200 ${NEU_ERR_TAG_TYPE_NOT_SUPPORT}
${input_bit_1} ${input_bit_1_name_wrong} ${modbus_node} 200 ${NEU_ERR_TAG_NOT_EXIST}
Update to be a static, it will be success
[Template] Update a ${tag} by a ${tag_update} named ${tag_name} from ${node} , it can read and write, using ${check} to check the ${value}, it will be success.
${hold_int16} ${hold_int16_static} hold_int16 ${modbus_node} Compare Tag Value As Int 42
${input_bit_1} ${input_bit_1_attribute_unmatch} ${modbus_tcp_node} 200 ${NEU_ERR_TAG_ATTRIBUTE_NOT_SUPPORT}
${input_bit_1} ${input_bit_1_type_unmatch} ${modbus_tcp_node} 200 ${NEU_ERR_TAG_TYPE_NOT_SUPPORT}
${input_bit_1} ${input_bit_1_name_wrong} ${modbus_tcp_node} 200 ${NEU_ERR_TAG_NOT_EXIST}
Write a value to a tag that don't exit in plc or neuron, it will be failure.
[Template] Write a ${value} to a ${tag} with named ${tag_name} that don't exist in neuron or plc from ${node}, it will be failure.
12 ${input_bit_1} input ${modbus_node}
12 ${input_bit_1} input ${modbus_tcp_node}
Write value to a tag without write attribute, it will be failure.
[Template] Write a ${value} to a ${tag} named ${tag_name} that without write attribute from ${node}, it will be failure.
1 ${input_bit_1} input_bit_1 ${modbus_node}
1 ${input_bit_1} input_bit_1 ${modbus_tcp_node}
Write and read a value to a tag when the node is stoped, it will be failure.
[Template] Write and read a ${value} to a ${tag} named ${tag_name} when the ${node} is stoped, it will be failure.
1 ${hold_int16} hold_int16 ${modbus_node}
1 ${hold_int16} hold_int16 ${modbus_tcp_node}
*** Keywords ***
Modbus Test Setup
@@ -182,17 +169,17 @@ Modbus Test Setup
Start Neuronx
${res}= Add Node ${modbus_node} ${PLUGIN-MODBUS-TCP}
${res}= Add Node ${modbus_tcp_node} ${PLUGIN-MODBUS-TCP}
Check Response Status ${res} 200
${res}= Node Setting ${modbus_node} ${MODBUS_TCP_CONFIG}
${res}= Node Setting ${modbus_tcp_node} ${MODBUS_TCP_CONFIG}
Check Response Status ${res} 200
${state}= Get Node State ${modbus_node}
${state}= Get Node State ${modbus_tcp_node}
Should Be Equal As Integers ${state}[running] ${NODE_STATE_RUNNING}
Should Be Equal As Integers ${state}[link] ${NODE_LINK_STATE_DISCONNECTED}
# Should Be Equal As Integers ${state}[link] ${NODE_LINK_STATE_CONNECTED}
Modbus Test Teardown
Del Node ${modbus_node}
Del Node ${modbus_tcp_node}
Stop Neuronx
Terminate All Processes kill=false
+3 -3
View File
@@ -72,8 +72,8 @@ Get the deleted adapter after stopping and restarting Neuron, it should return f
Get the setted adapters information after stopping and restarting Neuron, it should return the correct information
${process} = Neuron Start Running
Node Setting ${modbus-tcp-1} {"host": "127.0.0.1", "port": 502, "timeout": 3000}
Node Setting ${modbus-tcp-2} {"host": "127.0.0.1", "port": 502, "timeout": 3000}
Node Setting ${modbus-tcp-1} {"host": "127.0.0.1", "port": 502, "timeout": 3000, "connection_mode": 0, "interval": 20}
Node Setting ${modbus-tcp-2} {"host": "127.0.0.1", "port": 502, "timeout": 3000, "connection_mode": 0, "interval": 20}
Node Setting ${mqtt-1} {"client-id":"123456", "upload-topic":"/neuron/mqtt-1/upload", "heartbeat-topic": "/neuron/mqtt-1/heartbeat","format": 0,"cache-mem-size": 10,"cache-disk-size": 10,"ssl":false,"host":"127.0.0.1","port":1883,"username":"admin","password":"0000","ca":"", "cert":"", "key":"", "keypass":""}
Neuron Stop Running ${process}
@@ -117,7 +117,7 @@ Get the setted adapters information after stopping and restarting Neuron, it sho
Get the updated adapters settings information after stopping and restarting Neuron, it should return the correct information
${process} = Neuron Start Running
Node Setting ${modbus-tcp-2} {"host": "127.0.0.1", "port": 502, "timeout": 3000}
Node Setting ${modbus-tcp-2} {"host": "127.0.0.1", "port": 502, "timeout": 3000, "connection_mode": 0, "interval": 20}
Neuron Stop Running ${process}
${process} = Neuron Start Running
+1 -1
View File
@@ -3,7 +3,7 @@ Library REST http://127.0.0.1:7000
Resource common.resource
*** Variables ***
${PLUGIN-MODBUS-TCP} Modbus TCP community
${PLUGIN-MODBUS-TCP} Modbus TCP
${PLUGIN-MQTT} MQTT
${PLUGIN-EKUIPER} eKuiper
+2 -2
View File
@@ -17,7 +17,7 @@ Resource simulator.resource
${test_data_dir} ft/data
${PLUGIN_MODBUS_TCP} Modbus TCP community
${PLUGIN_MODBUS_TCP} Modbus TCP
${PLUGIN_MQTT} MQTT
${MQTT_CMD_NONE}
@@ -39,7 +39,7 @@ ${MQTT_CONFIG} {"client-id": "${MQTT_CLIENTID}", "u
... "ssl": false, "host": "${MQTT_CONFIG_HOST}", "port": ${MQTT_CONFIG_PORT},
... "username": "", "password": "", "ca":"", "cert":"", "key":"", "keypass":""}
${MODBUS_TCP_CONFIG} {"host": "127.0.0.1", "port": 60502, "timeout": 3000}
${MODBUS_TCP_CONFIG} {"host": "127.0.0.1", "port": 60502, "connection_mode": 0, "timeout": 3000, "interval": 20}
*** Variables ***
${neuron_process}
+5 -9
View File
@@ -70,13 +70,9 @@ Read some ${tags} from ${node}, it will be success.
Check Response Status ${res} 200
Check Error Code ${res} ${NEU_ERR_SUCCESS}
Log ${tags}
FOR ${tag} IN @{tags}
${res}= Add Tags ${node} group ${tag}
Check Response Status ${res} 200
Check Error Code ${res} ${NEU_ERR_SUCCESS}
END
${res}= Add Tags ${node} group ${tags}
Check Response Status ${res} 200
Check Error Code ${res} ${NEU_ERR_SUCCESS}
Sleep 290ms
${res}= Read Tags ${node} group
@@ -238,7 +234,7 @@ Write a ${value} to a ${tag} with named ${tag_name} that don't exist in neuron o
[Teardown] Del Group ${node} group
Read a ${tag} with named ${tag_name} that don't exist in plc form ${node}, it will be failure.
Read a ${tag} with named ${tag_name} that don't exist in plc form ${node}, it will be failure ${error}.
[Tags] Driver 15
${res}= Add Group ${node} group 100
@@ -252,7 +248,7 @@ Read a ${tag} with named ${tag_name} that don't exist in plc form ${node}, it wi
Sleep 290ms
${res}= Read Tags ${node} group
Check Response Status ${res} 200
Check Tag Error As Int ${res} ${tag_name} ${NEU_ERR_TAG_NOT_EXIST}
Check Tag Error As Int ${res}[tags] ${tag_name} ${error}
[Teardown] Del Group ${node} group
@@ -40,6 +40,12 @@ typedef enum neu_conn_tty_baud {
NEU_CONN_TTY_BAUD_9600,
NEU_CONN_TTY_BAUD_4800,
NEU_CONN_TTY_BAUD_2400,
NEU_CONN_TTY_BAUD_1800,
NEU_CONN_TTY_BAUD_1200,
NEU_CONN_TTY_BAUD_600,
NEU_CONN_TTY_BAUD_300,
NEU_CONN_TTY_BAUD_200,
NEU_CONN_TTY_BAUD_150,
} neu_conn_tty_baud_e;
typedef enum neu_conn_tty_parity {
+1
View File
@@ -154,6 +154,7 @@ typedef struct {
unsigned cpu_cores;
size_t mem_total_bytes;
size_t mem_used_bytes;
size_t mem_cache_bytes;
size_t disk_size_gibibytes;
size_t disk_used_gibibytes;
size_t disk_avail_gibibytes;
@@ -0,0 +1,45 @@
/**
* NEURON IIoT System for Industry 4.0
* Copyright (C) 2020-2023 EMQ Technologies Co., Ltd All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
BEGIN TRANSACTION;
-- Update default data-stream-processing node setting when
-- the data-stream-processing node exists and is at init state.
-- The init state implies the user has not provide any setting yet.
INSERT INTO
settings (node_name, setting)
SELECT
name,
'{"params":{"host":"0.0.0.0","port":7081}}'
FROM
nodes
WHERE
name = 'data-stream-processing'
and state = 1;
-- Update default data-stream-processing node to running state when
-- the data-stream-processing node exists and is at init state.
UPDATE
nodes
SET
state = 3
where
name = 'data-stream-processing'
and state = 1;
COMMIT;
+8
View File
@@ -32,6 +32,10 @@ static void plugin_group_free(neu_plugin_group_t *pgp);
int file_group_timer(neu_plugin_t *plugin, neu_plugin_group_t *group)
{
neu_adapter_update_metric_cb_t update_metric =
plugin->common.adapter_callbacks->update_metric;
int64_t rtt = NEU_METRIC_LAST_RTT_MS_MAX;
struct file_group_data *gd = NULL;
int length = plugin->file_length;
@@ -83,6 +87,8 @@ int file_group_timer(neu_plugin_t *plugin, neu_plugin_group_t *group)
strncpy(dvalue.value.str, buf, strlen(buf));
fclose(f);
rtt = 1;
dvalue_result:
plugin->common.adapter_callbacks->driver.update(
plugin->common.adapter, group->group_name, tag->name, dvalue);
@@ -90,6 +96,8 @@ int file_group_timer(neu_plugin_t *plugin, neu_plugin_group_t *group)
free(buf);
}
update_metric(plugin->common.adapter, NEU_METRIC_LAST_RTT_MS, rtt, NULL);
return 0;
}
+28 -8
View File
@@ -3,13 +3,33 @@ set(LIBRARY_OUTPUT_PATH "${CMAKE_BINARY_DIR}/plugins")
set(MODBUS_SRC modbus.c modbus_point.c modbus_req.c modbus_stack.c)
set(CMAKE_BUILD_RPATH ./)
file(COPY ${CMAKE_SOURCE_DIR}/plugins/modbus/modbus-tcp-community.json DESTINATION ${CMAKE_BINARY_DIR}/plugins/schema/)
file(COPY ${CMAKE_SOURCE_DIR}/plugins/modbus/modbus-tcp.json DESTINATION ${CMAKE_BINARY_DIR}/plugins/schema/)
file(COPY ${CMAKE_SOURCE_DIR}/plugins/modbus/modbus-rtu.json DESTINATION ${CMAKE_BINARY_DIR}/plugins/schema/)
file(COPY ${CMAKE_SOURCE_DIR}/plugins/modbus/modbus-qh-tcp.json DESTINATION ${CMAKE_BINARY_DIR}/plugins/schema/)
# modbus rtu plugin
set(MODBUS_TCP_PLUGIN plugin-modbus-tcp-comm)
set(MODBUS_TCP_PLUGIN_SOURCES modbus_tcp.c
set(MODBUS_RTU_PLUGIN plugin-modbus-rtu)
set(MODBUS_RTU_PLUGIN_SOURCES modbus_rtu.c
${MODBUS_SRC})
add_library(${MODBUS_RTU_PLUGIN} SHARED)
target_include_directories(${MODBUS_RTU_PLUGIN} PRIVATE ${CMAKE_SOURCE_DIR}/include/neuron)
target_sources(${MODBUS_RTU_PLUGIN} PRIVATE ${MODBUS_RTU_PLUGIN_SOURCES})
target_link_libraries(${MODBUS_RTU_PLUGIN} neuron-base)
# modbus tcp plugin
set(MODBUS_TCP_PLUS_PLUGIN plugin-modbus-tcp)
set(MODBUS_TCP_PLUS_PLUGIN_SOURCES modbus_tcp.c
${MODBUS_SRC})
add_library(${MODBUS_TCP_PLUGIN} SHARED)
target_include_directories(${MODBUS_TCP_PLUGIN} PRIVATE ${CMAKE_SOURCE_DIR}/include/neuron)
target_sources(${MODBUS_TCP_PLUGIN} PRIVATE ${MODBUS_TCP_PLUGIN_SOURCES})
target_link_libraries(${MODBUS_TCP_PLUGIN} neuron-base)
add_library(${MODBUS_TCP_PLUS_PLUGIN} SHARED)
target_include_directories(${MODBUS_TCP_PLUS_PLUGIN} PRIVATE ${CMAKE_SOURCE_DIR}/include/neuron)
target_sources(${MODBUS_TCP_PLUS_PLUGIN} PRIVATE ${MODBUS_TCP_PLUS_PLUGIN_SOURCES})
target_link_libraries(${MODBUS_TCP_PLUS_PLUGIN} neuron-base)
# modbus qh tcp plugin
set(MODBUS_TCP_QH_PLUGIN plugin-modbus-qh-tcp)
set(MODBUS_TCP_QH_PLUGIN_SOURCES modbus_qh_tcp.c
${MODBUS_SRC})
add_library(${MODBUS_TCP_QH_PLUGIN} SHARED)
target_include_directories(${MODBUS_TCP_QH_PLUGIN} PRIVATE ${CMAKE_SOURCE_DIR}/include/neuron)
target_sources(${MODBUS_TCP_QH_PLUGIN} PRIVATE ${MODBUS_TCP_QH_PLUGIN_SOURCES})
target_link_libraries(${MODBUS_TCP_QH_PLUGIN} neuron-base)
+104
View File
@@ -0,0 +1,104 @@
{
"tag_regex": [
{
"type": 3,
"regex": "^[0-9]+![3-4][0-9]+(#B|#L|)$"
},
{
"type": 4,
"regex": "^[0-9]+![3-4][0-9]+(#B|#L|)$"
},
{
"type": 5,
"regex": "^[0-9]+![3-4][0-9]+(#BB|#BL|#LL|#LB|)$"
},
{
"type": 6,
"regex": "^[0-9]+![3-4][0-9]+(#BB|#BL|#LL|#LB|)$"
},
{
"type": 7,
"regex": "^[0-9]+![3-4][0-9]+(#B|#L|)$"
},
{
"type": 8,
"regex": "^[0-9]+![3-4][0-9]+(#B|#L|)$"
},
{
"type": 9,
"regex": "^[0-9]+![3-4][0-9]+(#BB|#BL|#LL|#LB|)$"
},
{
"type": 10,
"regex": "^[0-9]+![3-4][0-9]+(#B|#L|)$"
},
{
"type": 11,
"regex": "^[0-9]+!([0-1][0-9]+|[3-4][0-9]+\\.([0-9]|[0-1][0-5]))$"
},
{
"type": 13,
"regex": "^[0-9]+![3-4][0-9]+\\.[0-9]+(H|L|)$"
}
],
"group_interval": 100,
"host": {
"name": "IP Address",
"name_zh": "IP地址",
"description": "Local IP in server mode, remote device IP in client mode",
"description_zh": "服务端模式中填写本地 IP,客户端模式中填写目标设备 IP",
"attribute": "required",
"type": "string",
"valid": {
"regex": "/^((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)$/",
"length": 30
}
},
"port": {
"name": "Port",
"name_zh": "端口号",
"description": "Local port in server mode, remote device port in client mode",
"description_zh": "服务端模式中填写本地端口号,客户端模式中填写远程设备端口号",
"attribute": "required",
"type": "int",
"default": 502,
"valid": {
"min": 1,
"max": 65535
}
},
"timeout": {
"name": "Connection Timeout",
"name_zh": "连接超时时间",
"description": "Connection timeout(ms)",
"description_zh": "连接超时时间,单位为毫秒",
"attribute": "required",
"type": "int",
"default": 3000,
"valid": {
"min": 1000,
"max": 65535
}
},
"connection_mode": {
"name": "Connection Mode",
"name_zh": "连接模式",
"description": "Neuron as the client, or as the server",
"description_zh": "Neuron 作为客户端或服务端",
"attribute": "required",
"type": "map",
"default": 0,
"valid": {
"map": [
{
"key": "Client",
"value": 0
},
{
"key": "Server",
"value": 1
}
]
}
}
}
+305
View File
@@ -0,0 +1,305 @@
{
"tag_regex": [
{
"type": 3,
"regex": "^[0-9]+![3-4][0-9]+(#B|#L|)$"
},
{
"type": 4,
"regex": "^[0-9]+![3-4][0-9]+(#B|#L|)$"
},
{
"type": 5,
"regex": "^[0-9]+![3-4][0-9]+(#BB|#BL|#LL|#LB|)$"
},
{
"type": 6,
"regex": "^[0-9]+![3-4][0-9]+(#BB|#BL|#LL|#LB|)$"
},
{
"type": 7,
"regex": "^[0-9]+![3-4][0-9]+(#B|#L|)$"
},
{
"type": 8,
"regex": "^[0-9]+![3-4][0-9]+(#B|#L|)$"
},
{
"type": 9,
"regex": "^[0-9]+![3-4][0-9]+(#BB|#BL|#LL|#LB|)$"
},
{
"type": 10,
"regex": "^[0-9]+![3-4][0-9]+(#B|#L|)$"
},
{
"type": 11,
"regex": "^[0-9]+!([0-1][0-9]+|[3-4][0-9]+\\.([0-9]|[0-1][0-5]))$"
},
{
"type": 13,
"regex": "^[0-9]+![3-4][0-9]+\\.[0-9]+(H|L|)$"
}
],
"group_interval": 1000,
"link": {
"name": "Physical Link",
"name_zh": "物理链路",
"description": "Serial or Ethernet",
"description_zh": "串口连接或以太网连接",
"attribute": "required",
"type": "map",
"default": 0,
"valid": {
"map": [
{
"key": "Serial",
"value": 0
},
{
"key": "TCP",
"value": 1
}
]
}
},
"timeout": {
"name": "Connection Timeout",
"name_zh": "连接超时时间",
"description": "Connection timeout(ms)",
"description_zh": "连接超时时间,单位为毫秒",
"attribute": "required",
"type": "int",
"default": 3000,
"valid": {
"min": 1000,
"max": 30000
}
},
"interval": {
"name": "Send Interval",
"name_zh": "指令发送间隔",
"description": "Send reading instruction interval(ms)",
"description_zh": "发送读指令时间间隔,单位为毫秒",
"attribute": "required",
"type": "int",
"default": 20,
"valid": {
"min": 0,
"max": 3000
}
},
"device": {
"name": "Serial Device",
"name_zh": "串口设备",
"description": "Serial device path",
"description_zh": "串口设备路径",
"attribute": "required",
"type": "string",
"condition": {
"field": "link",
"value": 0
},
"valid": {
"length": 30
}
},
"stop": {
"name": "Stop Bits",
"name_zh": "停止位",
"description": "Stop bits",
"description_zh": "停止位",
"attribute": "required",
"type": "map",
"default": 0,
"valid": {
"map": [
{
"key": "1",
"value": 0
},
{
"key": "2",
"value": 1
}
]
},
"condition": {
"field": "link",
"value": 0
}
},
"parity": {
"name": "Parity",
"name_zh": "校验位",
"description": "Parity",
"description_zh": "校验位",
"attribute": "required",
"type": "map",
"default": 0,
"valid": {
"map": [
{
"key": "none",
"value": 0
},
{
"key": "odd",
"value": 1
},
{
"key": "even",
"value": 2
},
{
"key": "mark",
"value": 3
},
{
"key": "space",
"value": 4
}
]
},
"condition": {
"field": "link",
"value": 0
}
},
"baud": {
"name": "Baud Rate",
"name_zh": "波特率",
"description": "Baud rate",
"description_zh": "波特率",
"attribute": "required",
"type": "map",
"default": 4,
"valid": {
"map": [
{
"key": "2400",
"value": 6
},
{
"key": "4800",
"value": 5
},
{
"key": "9600",
"value": 4
},
{
"key": "19200",
"value": 3
},
{
"key": "38400",
"value": 2
},
{
"key": "57600",
"value": 1
},
{
"key": "115200",
"value": 0
}
]
},
"condition": {
"field": "link",
"value": 0
}
},
"data": {
"name": "Data Bits",
"name_zh": "数据位",
"description": "Data bits",
"description_zh": "数据位",
"attribute": "required",
"type": "map",
"default": 3,
"valid": {
"map": [
{
"key": "5",
"value": 0
},
{
"key": "6",
"value": 1
},
{
"key": "7",
"value": 2
},
{
"key": "8",
"value": 3
}
]
},
"condition": {
"field": "link",
"value": 0
}
},
"host": {
"name": "IP Address",
"name_zh": "IP地址",
"description": "Local IP in server mode, remote device IP in client mode",
"description_zh": "服务端模式中填写本地 IP,客户端模式中填写目标设备 IP",
"attribute": "required",
"type": "string",
"valid": {
"regex": "/^((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)$/",
"length": 30
},
"condition": {
"field": "link",
"value": 1
}
},
"port": {
"name": "Port",
"name_zh": "端口号",
"description": "Local port in server mode, remote device port in client mode",
"description_zh": "服务端模式中填写本地端口号,客户端模式中填写远程设备端口号",
"attribute": "required",
"type": "int",
"default": 502,
"valid": {
"min": 1,
"max": 65535
},
"condition": {
"field": "link",
"value": 1
}
},
"connection_mode": {
"name": "Connection Mode",
"name_zh": "连接模式",
"description": "Neuron as the client, or as the server",
"description_zh": "Neuron 作为客户端或服务端",
"attribute": "required",
"type": "map",
"default": 0,
"valid": {
"map": [
{
"key": "Client",
"value": 0
},
{
"key": "Server",
"value": 1
}
]
},
"condition": {
"field": "link",
"value": 1
}
}
}
-67
View File
@@ -1,67 +0,0 @@
{
"tag_regex": [
{
"type": 3,
"regex": "^[0-9]+![3-4][0-9]+(#B|#L|)$"
},
{
"type": 4,
"regex": "^[0-9]+![3-4][0-9]+(#B|#L|)$"
},
{
"type": 5,
"regex": "^[0-9]+![3-4][0-9]+(#BB|#BL|#LL|#LB|)$"
},
{
"type": 6,
"regex": "^[0-9]+![3-4][0-9]+(#BB|#BL|#LL|#LB|)$"
},
{
"type": 9,
"regex": "^[0-9]+![3-4][0-9]+(#BB|#BL|#LL|#LB|)$"
},
{
"type": 11,
"regex": "^[0-9]+!([0-1][0-9]+|[3-4][0-9]+\\.([0-9]|[0-1][0-5]))$"
}
],
"group_interval": 1000,
"host": {
"name": "Device IP Address",
"name_zh": "设备IP地址",
"description": "Target device IPv4 address",
"description_zh": "目标设备IPv4地址",
"attribute": "required",
"type": "string",
"valid": {
"regex": "/^((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)$/",
"length": 30
}
},
"port": {
"name": "Device Port",
"name_zh": "设备端口",
"description": "Target device port number",
"description_zh": "目标设备端口号",
"attribute": "required",
"type": "int",
"default": 502,
"valid": {
"min": 1,
"max": 65535
}
},
"timeout": {
"name": "Recv Timeout",
"name_zh": "接收超时时间",
"description": "Recv msg timeout(ms)",
"description_zh": "接收消息超时时间(毫秒)",
"attribute": "required",
"type": "int",
"default": 3000,
"valid": {
"min": 1000,
"max": 65535
}
}
}
+117
View File
@@ -0,0 +1,117 @@
{
"tag_regex": [
{
"type": 3,
"regex": "^[0-9]+![3-4][0-9]+(#B|#L|)$"
},
{
"type": 4,
"regex": "^[0-9]+![3-4][0-9]+(#B|#L|)$"
},
{
"type": 5,
"regex": "^[0-9]+![3-4][0-9]+(#BB|#BL|#LL|#LB|)$"
},
{
"type": 6,
"regex": "^[0-9]+![3-4][0-9]+(#BB|#BL|#LL|#LB|)$"
},
{
"type": 7,
"regex": "^[0-9]+![3-4][0-9]+(#B|#L|)$"
},
{
"type": 8,
"regex": "^[0-9]+![3-4][0-9]+(#B|#L|)$"
},
{
"type": 9,
"regex": "^[0-9]+![3-4][0-9]+(#BB|#BL|#LL|#LB|)$"
},
{
"type": 10,
"regex": "^[0-9]+![3-4][0-9]+(#B|#L|)$"
},
{
"type": 11,
"regex": "^[0-9]+!([0-1][0-9]+|[3-4][0-9]+\\.([0-9]|[0-1][0-5]))$"
},
{
"type": 13,
"regex": "^[0-9]+![3-4][0-9]+\\.[0-9]+(H|L|)$"
}
],
"group_interval": 1000,
"connection_mode": {
"name": "Connection Mode",
"name_zh": "连接模式",
"description": "Neuron as the client, or as the server",
"description_zh": "Neuron 作为客户端或服务端",
"attribute": "required",
"type": "map",
"default": 0,
"valid": {
"map": [
{
"key": "Client",
"value": 0
},
{
"key": "Server",
"value": 1
}
]
}
},
"interval": {
"name": "Send Interval",
"name_zh": "指令发送间隔",
"description": "Send reading instruction interval(ms)",
"description_zh": "发送读指令时间间隔,单位为毫秒",
"attribute": "required",
"type": "int",
"default": 20,
"valid": {
"min": 0,
"max": 3000
}
},
"host": {
"name": "IP Address",
"name_zh": "IP地址",
"description": "Local IP in server mode, remote device IP in client mode",
"description_zh": "服务端模式中填写本地 IP,客户端模式中填写目标设备 IP",
"attribute": "required",
"type": "string",
"valid": {
"regex": "/^((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)$/",
"length": 30
}
},
"port": {
"name": "Port",
"name_zh": "端口号",
"description": "Local port in server mode, remote device port in client mode",
"description_zh": "服务端模式中填写本地端口号,客户端模式中填写远程设备端口号",
"attribute": "required",
"type": "int",
"default": 502,
"valid": {
"min": 1,
"max": 65535
}
},
"timeout": {
"name": "Connection Timeout",
"name_zh": "连接超时时间",
"description": "Connection timeout(ms)",
"description_zh": "连接超时时间,单位为毫秒",
"attribute": "required",
"type": "int",
"default": 3000,
"valid": {
"min": 1000,
"max": 65535
}
}
}
+53
View File
@@ -23,6 +23,25 @@
#include "modbus.h"
static uint16_t calcrc(uint8_t *buf, int len)
{
uint16_t crc = 0xffff;
uint16_t crcpoly = 0xa001;
for (int i = 0; i < len; i++) {
uint8_t x = buf[i];
crc ^= x;
for (int k = 0; k < 8; k++) {
uint16_t usepoly = crc & 0x1;
crc >>= 1;
if (usepoly)
crc ^= crcpoly;
}
}
return crc;
}
void modbus_header_wrap(neu_protocol_pack_buf_t *buf, uint16_t seq)
{
assert(neu_protocol_pack_buf_unused_size(buf) >=
@@ -149,6 +168,40 @@ int modbus_data_unwrap(neu_protocol_unpack_buf_t *buf,
return sizeof(struct modbus_data);
}
void modbus_crc_set(neu_protocol_pack_buf_t *buf)
{
uint16_t crc = calcrc(neu_protocol_pack_buf_get(buf),
neu_protocol_pack_buf_used_size(buf) - 2);
uint16_t *p_crc = (uint16_t *) neu_protocol_pack_buf_set(
buf, neu_protocol_pack_buf_used_size(buf) - 2, 2);
*p_crc = crc;
}
void modbus_crc_wrap(neu_protocol_pack_buf_t *buf)
{
assert(neu_protocol_pack_buf_unused_size(buf) >= sizeof(struct modbus_crc));
struct modbus_crc *crc = (struct modbus_crc *) neu_protocol_pack_buf(
buf, sizeof(struct modbus_crc));
crc->crc = 0;
}
int modbus_crc_unwrap(neu_protocol_unpack_buf_t *buf,
struct modbus_crc * out_crc)
{
struct modbus_crc *crc = (struct modbus_crc *) neu_protocol_unpack_buf(
buf, sizeof(struct modbus_crc));
if (crc == NULL) {
return 0;
}
*out_crc = *crc;
return sizeof(struct modbus_crc);
}
const char *modbus_area_to_str(modbus_area_e area)
{
switch (area) {
+9
View File
@@ -92,6 +92,15 @@ void modbus_data_wrap(neu_protocol_pack_buf_t *buf, uint8_t n_byte,
int modbus_data_unwrap(neu_protocol_unpack_buf_t *buf,
struct modbus_data * out_data);
struct modbus_crc {
uint16_t crc;
} __attribute__((packed));
void modbus_crc_set(neu_protocol_pack_buf_t *buf);
void modbus_crc_wrap(neu_protocol_pack_buf_t *buf);
int modbus_crc_unwrap(neu_protocol_unpack_buf_t *buf,
struct modbus_crc * out_crc);
const char *modbus_area_to_str(modbus_area_e area);
#endif
+10
View File
@@ -123,6 +123,16 @@ int modbus_tag_to_point(neu_datatag_t *tag, modbus_point_t *point)
point->n_register = 2;
}
break;
case NEU_TYPE_UINT64:
case NEU_TYPE_INT64:
case NEU_TYPE_DOUBLE:
if (point->area == MODBUS_AREA_COIL ||
point->area == MODBUS_AREA_INPUT) {
ret = NEU_ERR_TAG_TYPE_NOT_SUPPORT;
} else {
point->n_register = 4;
}
break;
case NEU_TYPE_STRING:
if (point->area == MODBUS_AREA_COIL ||
point->area == MODBUS_AREA_INPUT) {
+224
View File
@@ -0,0 +1,224 @@
/**
* NEURON IIoT System for Industry 4.0
* Copyright (C) 2020-2022 EMQ Technologies Co., Ltd All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
#include <stdlib.h>
#include <neuron.h>
#include "errcodes.h"
#include "modbus_point.h"
#include "modbus_req.h"
#include "modbus_stack.h"
static neu_plugin_t *driver_open(void);
static int driver_close(neu_plugin_t *plugin);
static int driver_init(neu_plugin_t *plugin);
static int driver_uninit(neu_plugin_t *plugin);
static int driver_start(neu_plugin_t *plugin);
static int driver_stop(neu_plugin_t *plugin);
static int driver_config(neu_plugin_t *plugin, const char *config);
static int driver_request(neu_plugin_t *plugin, neu_reqresp_head_t *head,
void *data);
static int driver_validate_tag(neu_plugin_t *plugin, neu_datatag_t *tag);
static int driver_group_timer(neu_plugin_t *plugin, neu_plugin_group_t *group);
static int driver_write(neu_plugin_t *plugin, void *req, neu_datatag_t *tag,
neu_value_u value);
static const neu_plugin_intf_funs_t plugin_intf_funs = {
.open = driver_open,
.close = driver_close,
.init = driver_init,
.uninit = driver_uninit,
.start = driver_start,
.stop = driver_stop,
.setting = driver_config,
.request = driver_request,
.driver.validate_tag = driver_validate_tag,
.driver.group_timer = driver_group_timer,
.driver.write_tag = driver_write,
};
const neu_plugin_module_t neu_plugin_module = {
.version = NEURON_PLUGIN_VER_1_0,
.schema = "modbus-qh-tcp",
.module_name = "Modbus TCP QH",
.module_descr =
"The modbus-qh-tcp plugin is an optimized version of modbus tcp, which "
"expands the packet description field to two bytes, limiting the "
"maximum length of Modbus tcp packets to 64KB",
.module_descr_zh =
"该插件是 modbus-tcp 插件的优化版,数据包描述字段扩展为连个字节,限制 "
"Modbus TCP 数据包的最大长度为 64KB。",
.intf_funs = &plugin_intf_funs,
.kind = NEU_PLUGIN_KIND_SYSTEM,
.type = NEU_NA_TYPE_DRIVER,
.display = true,
.single = false,
.timer_type = NEU_EVENT_TIMER_NOBLOCK,
};
static neu_plugin_t *driver_open(void)
{
neu_plugin_t *plugin = calloc(1, sizeof(neu_plugin_t));
neu_plugin_common_init(&plugin->common);
return plugin;
}
static int driver_close(neu_plugin_t *plugin)
{
free(plugin);
return 0;
}
static int driver_init(neu_plugin_t *plugin)
{
plugin->protocol = MODBUS_PROTOCOL_TCP;
plugin->events = neu_event_new();
plugin->stack = modbus_stack_create((void *) plugin, MODBUS_PROTOCOL_TCP,
modbus_send_msg, modbus_value_handle,
modbus_write_resp);
return 0;
}
static int driver_uninit(neu_plugin_t *plugin)
{
if (plugin->conn != NULL) {
neu_conn_destory(plugin->conn);
}
if (plugin->stack) {
modbus_stack_destroy(plugin->stack);
}
neu_event_close(plugin->events);
plog_info(plugin, "node: modbus uninit ok");
return 0;
}
static int driver_start(neu_plugin_t *plugin)
{
neu_conn_start(plugin->conn);
return 0;
}
static int driver_stop(neu_plugin_t *plugin)
{
neu_conn_stop(plugin->conn);
return 0;
}
static int driver_config(neu_plugin_t *plugin, const char *config)
{
int ret = 0;
char * err_param = NULL;
neu_json_elem_t port = { .name = "port", .t = NEU_JSON_INT };
neu_json_elem_t timeout = { .name = "timeout", .t = NEU_JSON_INT };
neu_json_elem_t host = { .name = "host", .t = NEU_JSON_STR };
neu_json_elem_t mode = { .name = "connection_mode", .t = NEU_JSON_INT };
neu_conn_param_t param = { 0 };
ret = neu_parse_param((char *) config, &err_param, 4, &port, &host, &mode,
&timeout);
if (ret != 0) {
plog_info(plugin, "config: %s, decode error: %s", config, err_param);
free(err_param);
return -1;
}
param.log = plugin->common.log;
if (mode.v.val_int == 1) {
param.type = NEU_CONN_TCP_SERVER;
param.params.tcp_server.ip = host.v.val_str;
param.params.tcp_server.port = port.v.val_int;
param.params.tcp_server.start_listen = modbus_tcp_server_listen;
param.params.tcp_server.stop_listen = modbus_tcp_server_stop;
param.params.tcp_server.timeout = timeout.v.val_int;
param.params.tcp_server.max_link = 1;
plugin->is_server = true;
} else {
param.type = NEU_CONN_TCP_CLIENT;
param.params.tcp_client.ip = host.v.val_str;
param.params.tcp_client.port = port.v.val_int;
param.params.tcp_client.timeout = timeout.v.val_int;
plugin->is_server = false;
}
plog_info(plugin, "config: host: %s, port: %" PRId64 ", mode: %" PRId64 "",
host.v.val_str, port.v.val_int, mode.v.val_int);
if (plugin->conn != NULL) {
plugin->conn = neu_conn_reconfig(plugin->conn, &param);
} else {
plugin->common.link_state = NEU_NODE_LINK_STATE_DISCONNECTED;
plugin->conn =
neu_conn_new(&param, (void *) plugin, modbus_conn_connected,
modbus_conn_disconnected);
}
free(host.v.val_str);
return 0;
}
static int driver_request(neu_plugin_t *plugin, neu_reqresp_head_t *head,
void *data)
{
(void) plugin;
(void) head;
(void) data;
return 0;
}
static int driver_validate_tag(neu_plugin_t *plugin, neu_datatag_t *tag)
{
modbus_point_t point = { 0 };
int ret = modbus_tag_to_point(tag, &point);
if (ret == 0) {
plog_info(
plugin,
"validate tag success, name: %s, address: %s, type: %d, slave id: "
"%d, start address: %d, n register: %d, area: %s",
tag->name, tag->address, tag->type, point.slave_id,
point.start_address, point.n_register,
modbus_area_to_str(point.area));
}
return ret;
}
static int driver_group_timer(neu_plugin_t *plugin, neu_plugin_group_t *group)
{
return modbus_group_timer(plugin, group, 0xffff);
}
static int driver_write(neu_plugin_t *plugin, void *req, neu_datatag_t *tag,
neu_value_u value)
{
return modbus_write(plugin, req, tag, value);
}
+108 -29
View File
@@ -16,14 +16,16 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
#include <time.h>
#include "modbus_point.h"
#include "modbus_stack.h"
#include "modbus_req.h"
struct modbus_group_data {
char * group;
UT_array * tags;
char * group;
modbus_read_cmd_sort_t *cmd_sort;
};
@@ -46,22 +48,76 @@ void modbus_conn_disconnected(void *data, int fd)
plugin->common.link_state = NEU_NODE_LINK_STATE_DISCONNECTED;
}
void modbus_tcp_server_listen(void *data, int fd)
{
struct neu_plugin * plugin = (struct neu_plugin *) data;
neu_event_io_param_t param = {
.cb = modbus_tcp_server_io_callback,
.fd = fd,
.usr_data = (void *) plugin,
};
plugin->tcp_server_io = neu_event_add_io(plugin->events, param);
}
void modbus_tcp_server_stop(void *data, int fd)
{
struct neu_plugin *plugin = (struct neu_plugin *) data;
(void) fd;
neu_event_del_io(plugin->events, plugin->tcp_server_io);
}
int modbus_tcp_server_io_callback(enum neu_event_io_type type, int fd,
void *usr_data)
{
neu_plugin_t *plugin = (neu_plugin_t *) usr_data;
switch (type) {
case NEU_EVENT_IO_READ: {
int client_fd = neu_conn_tcp_server_accept(plugin->conn);
if (client_fd > 0) {
plugin->client_fd = client_fd;
}
break;
}
case NEU_EVENT_IO_CLOSED:
case NEU_EVENT_IO_HUP:
plog_warn(plugin, "tcp server recv: %d, conn closed, fd: %d", type, fd);
neu_event_del_io(plugin->events, plugin->tcp_server_io);
neu_conn_disconnect(plugin->conn);
break;
}
return 0;
}
int modbus_send_msg(void *ctx, uint16_t n_byte, uint8_t *bytes)
{
neu_plugin_t *plugin = (neu_plugin_t *) ctx;
int ret = 0;
plog_send_protocol(plugin, bytes, n_byte);
return neu_conn_send(plugin->conn, bytes, n_byte);
if (plugin->is_server) {
ret = neu_conn_tcp_server_send(plugin->conn, plugin->client_fd, bytes,
n_byte);
} else {
ret = neu_conn_send(plugin->conn, bytes, n_byte);
}
return ret;
}
int modbus_group_timer(neu_plugin_t *plugin, neu_plugin_group_t *group,
uint16_t max_byte)
{
neu_conn_state_t state = { 0 };
neu_adapter_update_metric_cb_t update_metric =
plugin->common.adapter_callbacks->update_metric;
struct modbus_group_data *gd = NULL;
neu_conn_state_t state = { 0 };
int64_t rtt = NEU_METRIC_LAST_RTT_MS_MAX;
struct modbus_group_data *gd = NULL;
int64_t rtt = NEU_METRIC_LAST_RTT_MS_MAX;
if (group->user_data == NULL) {
gd = calloc(1, sizeof(struct modbus_group_data));
@@ -81,32 +137,39 @@ int modbus_group_timer(neu_plugin_t *plugin, neu_plugin_group_t *group,
gd->group = strdup(group->group_name);
gd->cmd_sort = modbus_tag_sort(gd->tags, max_byte);
} else {
gd = (struct modbus_group_data *) group->user_data;
}
gd = (struct modbus_group_data *) group->user_data;
plugin->plugin_group_data = gd;
for (uint16_t i = 0; i < gd->cmd_sort->n_cmd; i++) {
uint16_t response_size = 0;
int64_t start = neu_time_ms();
plugin->cmd_idx = i;
uint16_t response_size = 0;
uint64_t read_tms = neu_time_ms();
int ret = modbus_stack_read(
plugin->stack, gd->cmd_sort->cmd[i].slave_id,
gd->cmd_sort->cmd[i].area, gd->cmd_sort->cmd[i].start_address,
gd->cmd_sort->cmd[i].n_register, &response_size);
if (ret > 0) {
process_protocol_buf(plugin, response_size);
int64_t end = neu_time_ms();
if (end - start < rtt) {
rtt = end - start;
}
ret = process_protocol_buf(plugin, response_size);
rtt = neu_time_ms() - read_tms;
}
if (ret <= 0) {
modbus_value_handle(plugin, gd->cmd_sort->cmd[i].slave_id, 0, NULL,
NEU_ERR_PLUGIN_DISCONNECTED);
rtt = NEU_METRIC_LAST_RTT_MS_MAX;
}
if (plugin->interval > 0) {
struct timespec t1 = { .tv_sec = plugin->interval / 1000,
.tv_nsec = 1000 * 1000 *
(plugin->interval % 1000) };
struct timespec t2 = { 0 };
nanosleep(&t1, &t2);
}
}
state = neu_conn_state(plugin->conn);
update_metric(plugin->common.adapter, NEU_METRIC_SEND_BYTES,
state.send_bytes, NULL);
update_metric(plugin->common.adapter, NEU_METRIC_RECV_BYTES,
@@ -114,12 +177,11 @@ int modbus_group_timer(neu_plugin_t *plugin, neu_plugin_group_t *group,
update_metric(plugin->common.adapter, NEU_METRIC_LAST_RTT_MS, rtt, NULL);
update_metric(plugin->common.adapter, NEU_METRIC_GROUP_LAST_SEND_MSGS,
gd->cmd_sort->n_cmd, group->group_name);
return 0;
}
int modbus_value_handle(void *ctx, uint8_t slave_id, uint16_t n_byte,
uint8_t *bytes)
uint8_t *bytes, int error)
{
neu_plugin_t * plugin = (neu_plugin_t *) ctx;
struct modbus_group_data *gd =
@@ -127,11 +189,11 @@ int modbus_value_handle(void *ctx, uint8_t slave_id, uint16_t n_byte,
uint16_t start_address = gd->cmd_sort->cmd[plugin->cmd_idx].start_address;
uint16_t n_register = gd->cmd_sort->cmd[plugin->cmd_idx].n_register;
if (bytes == NULL) {
if (error != NEU_ERR_SUCCESS) {
neu_dvalue_t dvalue = { 0 };
dvalue.type = NEU_TYPE_ERROR;
dvalue.value.i32 = NEU_ERR_PLUGIN_DISCONNECTED;
dvalue.value.i32 = error;
plugin->common.adapter_callbacks->driver.update(
plugin->common.adapter, gd->group, NULL, dvalue);
return 0;
@@ -182,6 +244,11 @@ int modbus_value_handle(void *ctx, uint8_t slave_id, uint16_t n_byte,
case NEU_TYPE_UINT32:
dvalue.value.u32 = ntohl(dvalue.value.u32);
break;
case NEU_TYPE_DOUBLE:
case NEU_TYPE_INT64:
case NEU_TYPE_UINT64:
dvalue.value.u64 = neu_ntohll(dvalue.value.u64);
break;
case NEU_TYPE_BIT: {
switch ((*p_tag)->area) {
case MODBUS_AREA_HOLD_REGISTER:
@@ -196,7 +263,6 @@ int modbus_value_handle(void *ctx, uint8_t slave_id, uint16_t n_byte,
case MODBUS_AREA_INPUT:
break;
}
break;
}
case NEU_TYPE_STRING: {
@@ -218,7 +284,6 @@ int modbus_value_handle(void *ctx, uint8_t slave_id, uint16_t n_byte,
dvalue.value.str[0] = '?';
dvalue.value.str[1] = 0;
}
break;
}
default:
@@ -242,18 +307,23 @@ int modbus_write(neu_plugin_t *plugin, void *req, neu_datatag_t *tag,
switch (tag->type) {
case NEU_TYPE_UINT16:
case NEU_TYPE_INT16: {
case NEU_TYPE_INT16:
value.u16 = htons(value.u16);
n_byte = sizeof(uint16_t);
break;
}
case NEU_TYPE_FLOAT:
case NEU_TYPE_UINT32:
case NEU_TYPE_INT32:
case NEU_TYPE_FLOAT: {
value.u32 = htonl(value.u32);
n_byte = sizeof(uint32_t);
break;
}
case NEU_TYPE_DOUBLE:
case NEU_TYPE_INT64:
case NEU_TYPE_UINT64:
value.u64 = neu_htonll(value.u64);
n_byte = sizeof(uint64_t);
break;
case NEU_TYPE_BIT: {
n_byte = sizeof(uint8_t);
break;
@@ -318,12 +388,21 @@ static int process_protocol_buf(neu_plugin_t *plugin, uint16_t response_size)
neu_protocol_unpack_buf_t pbuf = { 0 };
ssize_t ret = 0;
ret = neu_conn_recv(plugin->conn, recv_buf, response_size);
if (plugin->is_server) {
ret = neu_conn_tcp_server_recv(plugin->conn, plugin->client_fd,
recv_buf, response_size);
} else {
ret = neu_conn_recv(plugin->conn, recv_buf, response_size);
}
if (ret > 0) {
if (ret == response_size) {
if (response_size < 512) {
plog_recv_protocol(plugin, recv_buf, ret);
}
neu_protocol_unpack_buf_init(&pbuf, recv_buf, ret);
plog_recv_protocol(plugin, recv_buf, ret);
ret = modbus_stack_recv(plugin->stack, &pbuf);
} else {
ret = -1;
}
free(recv_buf);
+11 -1
View File
@@ -33,18 +33,28 @@ struct neu_plugin {
uint16_t cmd_idx;
neu_event_io_t *tcp_server_io;
bool is_server;
bool is_serial;
int client_fd;
neu_events_t * events;
modbus_protocol_e protocol;
uint16_t interval;
};
void modbus_conn_connected(void *data, int fd);
void modbus_conn_disconnected(void *data, int fd);
void modbus_tcp_server_listen(void *data, int fd);
void modbus_tcp_server_stop(void *data, int fd);
int modbus_tcp_server_io_callback(enum neu_event_io_type type, int fd,
void *usr_data);
int modbus_group_timer(neu_plugin_t *plugin, neu_plugin_group_t *group,
uint16_t max_byte);
int modbus_send_msg(void *ctx, uint16_t n_byte, uint8_t *bytes);
int modbus_value_handle(void *ctx, uint8_t slave_id, uint16_t n_byte,
uint8_t *bytes);
uint8_t *bytes, int error);
int modbus_write(neu_plugin_t *plugin, void *req, neu_datatag_t *tag,
neu_value_u value);
int modbus_write_resp(void *ctx, void *req, int error);
+276
View File
@@ -0,0 +1,276 @@
/**
* NEURON IIoT System for Industry 4.0
* Copyright (C) 2020-2022 EMQ Technologies Co., Ltd All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
#include <stdlib.h>
#include <neuron.h>
#include "errcodes.h"
#include "modbus_point.h"
#include "modbus_req.h"
#include "modbus_stack.h"
static neu_plugin_t *driver_open(void);
static int driver_close(neu_plugin_t *plugin);
static int driver_init(neu_plugin_t *plugin);
static int driver_uninit(neu_plugin_t *plugin);
static int driver_start(neu_plugin_t *plugin);
static int driver_stop(neu_plugin_t *plugin);
static int driver_config(neu_plugin_t *plugin, const char *config);
static int driver_request(neu_plugin_t *plugin, neu_reqresp_head_t *head,
void *data);
static int driver_validate_tag(neu_plugin_t *plugin, neu_datatag_t *tag);
static int driver_group_timer(neu_plugin_t *plugin, neu_plugin_group_t *group);
static int driver_write(neu_plugin_t *plugin, void *req, neu_datatag_t *tag,
neu_value_u value);
static const neu_plugin_intf_funs_t plugin_intf_funs = {
.open = driver_open,
.close = driver_close,
.init = driver_init,
.uninit = driver_uninit,
.start = driver_start,
.stop = driver_stop,
.setting = driver_config,
.request = driver_request,
.driver.validate_tag = driver_validate_tag,
.driver.group_timer = driver_group_timer,
.driver.write_tag = driver_write,
};
const neu_plugin_module_t neu_plugin_module = {
.version = NEURON_PLUGIN_VER_1_0,
.schema = "modbus-rtu",
.module_name = "Modbus RTU",
.module_descr =
"This plugin is used to connect devices using modbus rtu protocol. "
"Support serial and TCP communication methods. When using TCP "
"communication, it is necessary to use the pass-through function of "
"DTU to convert serial data into IP data and transmit it through "
"the network. Selecting client/server is also supported.",
.module_descr_zh =
"该插件用于连接使用 modbus rtu 协议的设备。支持串口和 TCP "
"两种通信方式。使用 TCP 通信时,需要使用 DTU "
"的透传功能,将串口数据转为 IP "
"数据并通过网络传输。也支持选择客户端/服务端。",
.intf_funs = &plugin_intf_funs,
.kind = NEU_PLUGIN_KIND_SYSTEM,
.type = NEU_NA_TYPE_DRIVER,
.display = true,
.single = false,
};
static neu_plugin_t *driver_open(void)
{
neu_plugin_t *plugin = calloc(1, sizeof(neu_plugin_t));
neu_plugin_common_init(&plugin->common);
return plugin;
}
static int driver_close(neu_plugin_t *plugin)
{
free(plugin);
return 0;
}
static int driver_init(neu_plugin_t *plugin)
{
plugin->protocol = MODBUS_PROTOCOL_RTU;
plugin->events = neu_event_new();
plugin->stack = modbus_stack_create((void *) plugin, MODBUS_PROTOCOL_RTU,
modbus_send_msg, modbus_value_handle,
modbus_write_resp);
return 0;
}
static int driver_uninit(neu_plugin_t *plugin)
{
if (plugin->conn != NULL) {
neu_conn_destory(plugin->conn);
}
if (plugin->stack) {
modbus_stack_destroy(plugin->stack);
}
neu_event_close(plugin->events);
plog_info(plugin, "node: modbus uninit ok");
return 0;
}
static int driver_start(neu_plugin_t *plugin)
{
neu_conn_start(plugin->conn);
return 0;
}
static int driver_stop(neu_plugin_t *plugin)
{
neu_conn_stop(plugin->conn);
return 0;
}
static int driver_config(neu_plugin_t *plugin, const char *config)
{
int ret = 0;
char * err_param = NULL;
neu_conn_param_t param = { 0 };
neu_json_elem_t link = { .name = "link", .t = NEU_JSON_INT };
neu_json_elem_t timeout = { .name = "timeout", .t = NEU_JSON_INT };
neu_json_elem_t interval = { .name = "interval", .t = NEU_JSON_INT };
neu_json_elem_t mode = { .name = "connection_mode", .t = NEU_JSON_INT };
neu_json_elem_t host = { .name = "host", .t = NEU_JSON_STR };
neu_json_elem_t port = { .name = "port", .t = NEU_JSON_INT };
neu_json_elem_t device = { .name = "device", .t = NEU_JSON_STR };
neu_json_elem_t stop = { .name = "stop", .t = NEU_JSON_INT };
neu_json_elem_t parity = { .name = "parity", .t = NEU_JSON_INT };
neu_json_elem_t baud = { .name = "baud", .t = NEU_JSON_INT };
neu_json_elem_t data = { .name = "data", .t = NEU_JSON_INT };
ret = neu_parse_param((char *) config, &err_param, 3, &link, &timeout,
&interval);
if (ret != 0) {
plog_warn(plugin, "config: %s, decode link & timeout error: %s", config,
err_param);
free(err_param);
return -1;
}
plugin->interval = interval.v.val_int;
if (link.v.val_int == 0) {
ret = neu_parse_param((char *) config, &err_param, 5, &device, &stop,
&parity, &baud, &data);
} else {
ret = neu_parse_param((char *) config, &err_param, 3, &mode, &host,
&port);
}
if (ret != 0) {
plog_warn(plugin, "config: %s, decode error: %s", config, err_param);
free(err_param);
return -1;
}
param.log = plugin->common.log;
if (link.v.val_int == 0) {
param.type = NEU_CONN_TTY_CLIENT;
param.params.tty_client.device = device.v.val_str;
param.params.tty_client.baud = baud.v.val_int;
param.params.tty_client.data = data.v.val_int;
param.params.tty_client.parity = parity.v.val_int;
param.params.tty_client.stop = stop.v.val_int;
param.params.tty_client.timeout = timeout.v.val_int;
plugin->is_serial = true;
plog_info(plugin,
"config: device: %s, baud: %" PRId64 ", data: %" PRId64
", parity: %" PRId64 ", stop: %" PRId64 "",
device.v.val_str, baud.v.val_int, data.v.val_int,
parity.v.val_int, stop.v.val_int);
} else {
if (mode.v.val_int == 1) {
param.type = NEU_CONN_TCP_SERVER;
param.params.tcp_server.ip = host.v.val_str;
param.params.tcp_server.port = port.v.val_int;
param.params.tcp_server.start_listen = modbus_tcp_server_listen;
param.params.tcp_server.stop_listen = modbus_tcp_server_stop;
param.params.tcp_server.timeout = timeout.v.val_int;
param.params.tcp_server.max_link = 1;
plugin->is_server = true;
} else {
param.type = NEU_CONN_TCP_CLIENT;
param.params.tcp_client.ip = host.v.val_str;
param.params.tcp_client.port = port.v.val_int;
param.params.tcp_client.timeout = timeout.v.val_int;
plugin->is_server = false;
}
plog_info(plugin,
"config: host: %s, port: %" PRId64 ", mode: %" PRId64 "",
host.v.val_str, port.v.val_int, mode.v.val_int);
}
if (plugin->conn != NULL) {
plugin->conn = neu_conn_reconfig(plugin->conn, &param);
} else {
plugin->common.link_state = NEU_NODE_LINK_STATE_DISCONNECTED;
plugin->conn =
neu_conn_new(&param, (void *) plugin, modbus_conn_connected,
modbus_conn_disconnected);
}
if (link.v.val_int == 0) {
free(device.v.val_str);
} else {
free(host.v.val_str);
}
return 0;
}
static int driver_request(neu_plugin_t *plugin, neu_reqresp_head_t *head,
void *data)
{
(void) plugin;
(void) head;
(void) data;
return 0;
}
static int driver_validate_tag(neu_plugin_t *plugin, neu_datatag_t *tag)
{
modbus_point_t point = { 0 };
int ret = modbus_tag_to_point(tag, &point);
if (ret == 0) {
plog_debug(
plugin,
"validate tag success, name: %s, address: %s, type: %d, slave id: "
"%d, start address: %d, n register: %d, area: %s",
tag->name, tag->address, tag->type, point.slave_id,
point.start_address, point.n_register,
modbus_area_to_str(point.area));
}
return ret;
}
static int driver_group_timer(neu_plugin_t *plugin, neu_plugin_group_t *group)
{
return modbus_group_timer(plugin, group, 0xfa);
}
static int driver_write(neu_plugin_t *plugin, void *req, neu_datatag_t *tag,
neu_value_u value)
{
return modbus_write(plugin, req, tag, value);
}
+83 -20
View File
@@ -20,6 +20,7 @@
#include <neuron.h>
#include "modbus_req.h"
#include "modbus_stack.h"
struct modbus_stack {
@@ -28,10 +29,16 @@ struct modbus_stack {
modbus_stack_value value_fn;
modbus_stack_write_resp write_resp;
uint16_t seq;
modbus_protocol_e protocol;
uint16_t read_seq;
uint16_t write_seq;
uint8_t *buf;
uint16_t buf_size;
};
modbus_stack_t *modbus_stack_create(void *ctx, modbus_stack_send send_fn,
modbus_stack_t *modbus_stack_create(void *ctx, modbus_protocol_e protocol,
modbus_stack_send send_fn,
modbus_stack_value value_fn,
modbus_stack_write_resp write_resp)
{
@@ -41,12 +48,17 @@ modbus_stack_t *modbus_stack_create(void *ctx, modbus_stack_send send_fn,
stack->send_fn = send_fn;
stack->value_fn = value_fn;
stack->write_resp = write_resp;
stack->protocol = protocol;
stack->buf_size = 256;
stack->buf = calloc(stack->buf_size, 1);
return stack;
}
void modbus_stack_destroy(modbus_stack_t *stack)
{
free(stack->buf);
free(stack);
}
@@ -56,9 +68,14 @@ int modbus_stack_recv(modbus_stack_t *stack, neu_protocol_unpack_buf_t *buf)
struct modbus_code code = { 0 };
int ret = 0;
ret = modbus_header_unwrap(buf, &header);
if (ret <= 0) {
return ret;
if (stack->protocol == MODBUS_PROTOCOL_TCP) {
ret = modbus_header_unwrap(buf, &header);
if (ret < 0) {
plog_warn((neu_plugin_t *) stack->ctx, "try modbus rtu driver");
}
if (ret <= 0) {
return ret;
}
}
ret = modbus_code_unwrap(buf, &code);
@@ -78,8 +95,19 @@ int modbus_stack_recv(modbus_stack_t *stack, neu_protocol_unpack_buf_t *buf)
return ret;
}
bytes = neu_protocol_unpack_buf(buf, data.n_byte);
stack->value_fn(stack->ctx, code.slave_id, data.n_byte, bytes);
if (stack->protocol == MODBUS_PROTOCOL_TCP && data.n_byte == 0xff) {
bytes = neu_protocol_unpack_buf(buf,
header.len -
sizeof(struct modbus_code) -
sizeof(struct modbus_data));
stack->value_fn(stack->ctx, code.slave_id,
header.len - sizeof(struct modbus_code) -
sizeof(struct modbus_data),
bytes, 0);
} else {
bytes = neu_protocol_unpack_buf(buf, data.n_byte);
stack->value_fn(stack->ctx, code.slave_id, data.n_byte, bytes, 0);
}
break;
}
@@ -95,6 +123,14 @@ int modbus_stack_recv(modbus_stack_t *stack, neu_protocol_unpack_buf_t *buf)
break;
}
if (stack->protocol == MODBUS_PROTOCOL_RTU) {
struct modbus_crc crc = { 0 };
ret = modbus_crc_unwrap(buf, &crc);
if (ret <= 0) {
return ret;
}
}
return neu_protocol_unpack_buf_used_size(buf);
}
@@ -108,6 +144,9 @@ int modbus_stack_read(modbus_stack_t *stack, uint8_t slave_id,
neu_protocol_pack_buf_init(&pbuf, buf, sizeof(buf));
if (stack->protocol == MODBUS_PROTOCOL_RTU) {
modbus_crc_wrap(&pbuf);
}
modbus_address_wrap(&pbuf, start_address, n_reg);
switch (area) {
@@ -132,14 +171,23 @@ int modbus_stack_read(modbus_stack_t *stack, uint8_t slave_id,
*response_size += sizeof(struct modbus_code);
*response_size += sizeof(struct modbus_data);
modbus_header_wrap(&pbuf, stack->seq++);
*response_size += sizeof(struct modbus_header);
switch (stack->protocol) {
case MODBUS_PROTOCOL_TCP:
modbus_header_wrap(&pbuf, stack->read_seq++);
*response_size += sizeof(struct modbus_header);
break;
case MODBUS_PROTOCOL_RTU:
modbus_crc_set(&pbuf);
*response_size += 2;
break;
}
ret = stack->send_fn(stack->ctx, neu_protocol_pack_buf_used_size(&pbuf),
neu_protocol_pack_buf_get(&pbuf));
if (ret <= 0) {
stack->value_fn(stack->ctx, 0, 0, NULL);
stack->value_fn(stack->ctx, 0, 0, NULL, NEU_ERR_PLUGIN_DISCONNECTED);
}
return ret;
}
@@ -148,11 +196,14 @@ int modbus_stack_write(modbus_stack_t *stack, void *req, uint8_t slave_id,
uint16_t n_reg, uint8_t *bytes, uint8_t n_byte,
uint16_t *response_size)
{
uint8_t * buf = calloc(256, 1);
neu_protocol_pack_buf_t pbuf = { 0 };
int ret = 0;
static __thread neu_protocol_pack_buf_t pbuf = { 0 };
neu_protocol_pack_buf_init(&pbuf, buf, 256);
memset(stack->buf, 0, stack->buf_size);
neu_protocol_pack_buf_init(&pbuf, stack->buf, stack->buf_size);
if (stack->protocol == MODBUS_PROTOCOL_RTU) {
modbus_crc_wrap(&pbuf);
}
switch (area) {
case MODBUS_AREA_COIL:
@@ -169,22 +220,34 @@ int modbus_stack_write(modbus_stack_t *stack, void *req, uint8_t slave_id,
modbus_code_wrap(&pbuf, slave_id, MODBUS_WRITE_M_HOLD_REG);
break;
default:
assert(false);
stack->write_resp(stack->ctx, req, NEU_ERR_PLUGIN_TAG_NOT_ALLOW_WRITE);
break;
}
*response_size += sizeof(struct modbus_code);
*response_size += sizeof(struct modbus_address);
modbus_header_wrap(&pbuf, stack->seq++);
*response_size += sizeof(struct modbus_header);
switch (stack->protocol) {
case MODBUS_PROTOCOL_TCP:
modbus_header_wrap(&pbuf, stack->write_seq++);
*response_size += sizeof(struct modbus_header);
break;
case MODBUS_PROTOCOL_RTU:
modbus_crc_set(&pbuf);
*response_size += 2;
break;
}
ret = stack->send_fn(stack->ctx, neu_protocol_pack_buf_used_size(&pbuf),
neu_protocol_pack_buf_get(&pbuf));
int ret = stack->send_fn(stack->ctx, neu_protocol_pack_buf_used_size(&pbuf),
neu_protocol_pack_buf_get(&pbuf));
if (ret > 0) {
stack->write_resp(stack->ctx, req, NEU_ERR_SUCCESS);
} else {
stack->write_resp(stack->ctx, req, NEU_ERR_PLUGIN_DISCONNECTED);
}
free(buf);
return ret;
}
bool modbus_stack_is_rtu(modbus_stack_t *stack)
{
return stack->protocol == MODBUS_PROTOCOL_RTU;
}
+15 -8
View File
@@ -29,22 +29,29 @@ typedef struct modbus_stack modbus_stack_t;
typedef int (*modbus_stack_send)(void *ctx, uint16_t n_byte, uint8_t *bytes);
typedef int (*modbus_stack_value)(void *ctx, uint8_t slave_id, uint16_t n_byte,
uint8_t *bytes);
uint8_t *bytes, int error);
typedef int (*modbus_stack_write_resp)(void *ctx, void *req, int error);
modbus_stack_t *modbus_stack_create(void *ctx, modbus_stack_send send_fn,
typedef enum modbus_protocol {
MODBUS_PROTOCOL_TCP = 1,
MODBUS_PROTOCOL_RTU = 2,
} modbus_protocol_e;
modbus_stack_t *modbus_stack_create(void *ctx, modbus_protocol_e protocol,
modbus_stack_send send_fn,
modbus_stack_value value_fn,
modbus_stack_write_resp write_resp);
void modbus_stack_destroy(modbus_stack_t *stack);
int modbus_stack_recv(modbus_stack_t *stack, neu_protocol_unpack_buf_t *buf);
int modbus_stack_read(modbus_stack_t *stack, uint8_t slave_id,
enum modbus_area area, uint16_t start_address,
uint16_t n_reg, uint16_t *response_size);
int modbus_stack_write(modbus_stack_t *stack, void *req, uint8_t slave_id,
int modbus_stack_read(modbus_stack_t *stack, uint8_t slave_id,
enum modbus_area area, uint16_t start_address,
uint16_t n_reg, uint8_t *bytes, uint8_t n_byte,
uint16_t *response_size);
uint16_t n_reg, uint16_t *response_size);
int modbus_stack_write(modbus_stack_t *stack, void *req, uint8_t slave_id,
enum modbus_area area, uint16_t start_address,
uint16_t n_reg, uint8_t *bytes, uint8_t n_byte,
uint16_t *response_size);
bool modbus_stack_is_rtu(modbus_stack_t *stack);
#endif
+49 -30
View File
@@ -18,19 +18,22 @@
**/
#include <stdlib.h>
#include "neuron.h"
#include <neuron.h>
#include "errcodes.h"
#include "modbus_point.h"
#include "modbus_req.h"
#include "modbus_stack.h"
static neu_plugin_t *driver_open(void);
static int driver_close(neu_plugin_t *plugin);
static int driver_init(neu_plugin_t *plugin);
static int driver_uninit(neu_plugin_t *plugin);
static int driver_start(neu_plugin_t *plugin);
static int driver_stop(neu_plugin_t *plugin);
static int driver_config(neu_plugin_t *plugin, const char *config);
static int driver_close(neu_plugin_t *plugin);
static int driver_init(neu_plugin_t *plugin);
static int driver_uninit(neu_plugin_t *plugin);
static int driver_start(neu_plugin_t *plugin);
static int driver_stop(neu_plugin_t *plugin);
static int driver_config(neu_plugin_t *plugin, const char *config);
static int driver_request(neu_plugin_t *plugin, neu_reqresp_head_t *head,
void *data);
@@ -56,18 +59,18 @@ static const neu_plugin_intf_funs_t plugin_intf_funs = {
const neu_plugin_module_t neu_plugin_module = {
.version = NEURON_PLUGIN_VER_1_0,
.schema = "modbus-tcp-community",
.module_name = "Modbus TCP community",
.schema = "modbus-tcp",
.module_name = "Modbus TCP",
.module_descr =
"The modbus-tcp community plugin is used for devices connected using "
"the modbus protocol tcp mode.",
.module_descr_zh =
"modbus-tcp community 插件用于连接使用 modbus tcp 协议的设备",
"The modbus tcp plugin is an enhanced version of the modbus tcp "
"plugin, users can choose to connect as a client or a server.",
.module_descr_zh = "该插件是 modbus tcp "
"插件的加强版,用户可选择作为客户端连接,还是服务端连接",
.intf_funs = &plugin_intf_funs,
.kind = NEU_PLUGIN_KIND_SYSTEM,
.type = NEU_NA_TYPE_DRIVER,
.single = false,
.display = true,
.single = false,
};
static neu_plugin_t *driver_open(void)
@@ -88,9 +91,11 @@ static int driver_close(neu_plugin_t *plugin)
static int driver_init(neu_plugin_t *plugin)
{
plugin->events = neu_event_new();
plugin->stack = modbus_stack_create((void *) plugin, modbus_send_msg,
modbus_value_handle, modbus_write_resp);
plugin->protocol = MODBUS_PROTOCOL_TCP;
plugin->events = neu_event_new();
plugin->stack = modbus_stack_create((void *) plugin, MODBUS_PROTOCOL_TCP,
modbus_send_msg, modbus_value_handle,
modbus_write_resp);
return 0;
}
@@ -131,32 +136,46 @@ static int driver_config(neu_plugin_t *plugin, const char *config)
neu_json_elem_t port = { .name = "port", .t = NEU_JSON_INT };
neu_json_elem_t timeout = { .name = "timeout", .t = NEU_JSON_INT };
neu_json_elem_t host = { .name = "host", .t = NEU_JSON_STR };
neu_conn_param_t param = { 0 };
neu_json_elem_t interval = { .name = "interval", .t = NEU_JSON_INT };
neu_json_elem_t mode = { .name = "connection_mode", .t = NEU_JSON_INT };
neu_conn_param_t param = { 0 };
ret =
neu_parse_param((char *) config, &err_param, 3, &port, &host, &timeout);
ret = neu_parse_param((char *) config, &err_param, 5, &port, &host, &mode,
&timeout, &interval);
if (ret != 0) {
plog_warn(plugin, "config: %s, decode error: %s", (char *) config,
err_param);
plog_warn(plugin, "config: %s, decode error: %s", config, err_param);
free(err_param);
free(host.v.val_str);
return -1;
}
param.log = plugin->common.log;
param.type = NEU_CONN_TCP_CLIENT;
param.params.tcp_client.ip = host.v.val_str;
param.params.tcp_client.port = port.v.val_int;
param.params.tcp_client.timeout = timeout.v.val_int;
param.log = plugin->common.log;
plugin->interval = interval.v.val_int;
if (mode.v.val_int == 1) {
param.type = NEU_CONN_TCP_SERVER;
param.params.tcp_server.ip = host.v.val_str;
param.params.tcp_server.port = port.v.val_int;
param.params.tcp_server.start_listen = modbus_tcp_server_listen;
param.params.tcp_server.stop_listen = modbus_tcp_server_stop;
param.params.tcp_server.timeout = timeout.v.val_int;
param.params.tcp_server.max_link = 1;
plugin->is_server = true;
} else {
param.type = NEU_CONN_TCP_CLIENT;
param.params.tcp_client.ip = host.v.val_str;
param.params.tcp_client.port = port.v.val_int;
param.params.tcp_client.timeout = timeout.v.val_int;
plugin->is_server = false;
}
plog_info(plugin, "config: host: %s, port: %" PRId64 ", timeout: %" PRId64,
host.v.val_str, port.v.val_int, timeout.v.val_int);
plog_info(plugin, "config: host: %s, port: %" PRId64 ", mode: %" PRId64 "",
host.v.val_str, port.v.val_int, mode.v.val_int);
plugin->common.link_state = NEU_NODE_LINK_STATE_DISCONNECTED;
if (plugin->conn != NULL) {
plugin->conn = neu_conn_reconfig(plugin->conn, &param);
} else {
plugin->common.link_state = NEU_NODE_LINK_STATE_DISCONNECTED;
plugin->conn =
neu_conn_new(&param, (void *) plugin, modbus_conn_connected,
modbus_conn_disconnected);
+11 -4
View File
@@ -31,10 +31,10 @@
// clang-format off
#define METRIC_GLOBAL_TMPL \
"# HELP distro OS distribution\n" \
"distro %s\n" \
"# HELP kernel OS kernel version\n" \
"kernel %s\n" \
"# HELP os_info OS distro and kernel version\n" \
"# TYPE os_info gauge\n" \
"os_info[version=\"%s\"] 0\n" \
"os_info[kernel=\"%s\"] 0\n" \
"# HELP cpu_percent Total CPU utilisation percentage\n" \
"# TYPE cpu_percent gauge\n" \
"cpu_percent %u\n" \
@@ -47,6 +47,12 @@
"# HELP mem_used_bytes Used memory in bytes\n" \
"# TYPE mem_used_bytes gauge\n" \
"mem_used_bytes %zu\n" \
"# HELP mem_cache Memory buffer/cache size in bytes\n" \
"# TYPE mem_cache gauge\n" \
"mem_cache_bytes %zu\n" \
"# HELP rss_bytes RSS (Resident Set Size) in bytes\n" \
"# TYPE rss_bytes gauge\n" \
"rss_bytes %zu\n" \
"# HELP disk_size_gibibytes Disk size in gibibytes\n" \
"# TYPE disk_size_gibibytes counter\n" \
"disk_size_gibibytes %zu\n" \
@@ -141,6 +147,7 @@ static inline void gen_global_metrics(const neu_metrics_t *metrics,
{
fprintf(stream, METRIC_GLOBAL_TMPL, metrics->distro, metrics->kernel,
metrics->cpu_percent, metrics->cpu_cores, metrics->mem_total_bytes,
metrics->mem_used_bytes, metrics->mem_cache_bytes,
metrics->mem_used_bytes, metrics->disk_size_gibibytes,
metrics->disk_used_gibibytes, metrics->disk_avail_gibibytes,
metrics->core_dumped, metrics->uptime_seconds, metrics->north_nodes,
+22 -22
View File
@@ -1,9 +1,9 @@
{
"client-id": {
"name": "Client ID",
"name_zh": "客户端ID",
"description": "Neuron MQTT client id",
"description_zh": "Neuron MQTT客户端ID",
"name_zh": "客户端 ID",
"description": "MQTT client ID",
"description_zh": "MQTT 客户端 ID",
"type": "string",
"attribute": "required",
"default": "${node-name}",
@@ -27,7 +27,7 @@
"name": "Heartbeat Interval",
"name_zh": "心跳间隔",
"description": "Interval in seconds between two heartbeat messages. Setting to zero will disable heartbeat monitoring.(seconds)",
"description_zh": "两个心跳消息之间的间隔时间,设置为0将禁用心跳监控。(秒)",
"description_zh": "每个心跳消息之间的间隔时间(秒),设置为 0 将禁用心跳消息。",
"attribute": "required",
"type": "int",
"default": 0,
@@ -51,7 +51,7 @@
"name": "Broker Host",
"name_zh": "服务器地址",
"description": "MQTT broker host",
"description_zh": "MQTT服务器地址",
"description_zh": "MQTT 服务器地址",
"attribute": "required",
"type": "string",
"default": "broker.emqx.io",
@@ -63,7 +63,7 @@
"name": "Broker Port",
"name_zh": "服务器端口",
"description": "MQTT broker port",
"description_zh": "MQTT服务器端口",
"description_zh": "MQTT 服务器端口",
"attribute": "required",
"type": "int",
"default": 1883,
@@ -75,8 +75,8 @@
"username": {
"name": "Username",
"name_zh": "用户名",
"description": "User name",
"description_zh": "用户名",
"description": "MQTT user name",
"description_zh": "MQTT 用户名",
"attribute": "optional",
"type": "string",
"default": "",
@@ -87,8 +87,8 @@
"password": {
"name": "Password",
"name_zh": "密码",
"description": "Password",
"description_zh": "密码",
"description": "MQTT user password",
"description_zh": "MQTT 用户密码",
"attribute": "optional",
"type": "string",
"default": "",
@@ -100,7 +100,7 @@
"name": "SSL",
"name_zh": "SSL",
"description": "Enable SSL connection",
"description_zh": "是否启用SSL连接",
"description_zh": "是否启用 SSL 连接",
"attribute": "optional",
"type": "bool",
"default": false,
@@ -108,9 +108,9 @@
},
"ca": {
"name": "CA",
"name_zh": "CA证书",
"description": "CA certificate file",
"description_zh": "CA证书文件",
"name_zh": "CA 证书",
"description": "CA certificate which signs the server certificate",
"description_zh": "签发服务器证书的 CA 证书",
"attribute": "required",
"type": "file",
"condition": {
@@ -124,8 +124,8 @@
"cert": {
"name": "Client Cert",
"name_zh": "客户端证书",
"description": "Client x509 certificate file",
"description_zh": "客户端证书文件",
"description": "Client x509 certificate when using two way authentication",
"description_zh": "使用双向认证时,客户端的 x509 证书",
"attribute": "optional",
"type": "file",
"condition": {
@@ -139,8 +139,8 @@
"key": {
"name": "Client Private Key",
"name_zh": "客户端私钥",
"description": "Client private key file",
"description_zh": "客户端私钥文件",
"description": "Client private key when using two way authentication",
"description_zh": "使用双向认证时,客户端的私钥",
"attribute": "optional",
"type": "file",
"condition": {
@@ -152,10 +152,10 @@
}
},
"keypass": {
"name": "Keypass",
"name_zh": "私钥密码",
"description": "Private key password",
"description_zh": "私钥密码",
"name": "Client Private Key Password",
"name_zh": "客户端私钥密码",
"description": "Client private key password when using two way authentication with an encrypted client private key",
"description_zh": "使用加密的客户端私钥进行双向认证时,客户端私钥的密码",
"attribute": "optional",
"type": "string",
"condition": {
+62 -34
View File
@@ -1,9 +1,9 @@
{
"client-id": {
"name": "Client ID",
"name_zh": "客户端ID",
"description": "Neuron MQTT client id",
"description_zh": "Neuron MQTT客户端ID",
"name_zh": "客户端 ID",
"description": "MQTT client id",
"description_zh": "MQTT 客户端 ID",
"type": "string",
"attribute": "required",
"default": "${node-name}",
@@ -15,46 +15,71 @@
"name": "QoS Level",
"name_zh": "QoS 等级",
"description": "MQTT QoS level for message delivery",
"description_zh": "MQTT消息传输使用的服务质量等级",
"type": "int",
"description_zh": "MQTT 消息传输使用的服务质量等级",
"type": "map",
"attribute": "optional",
"default": 0,
"valid": {
"min": 0,
"max": 2
"map": [
{
"key": "QoS 0",
"value": 0
},
{
"key": "QoS 1",
"value": 1
},
{
"key": "QoS 2",
"value": 2
}
]
}
},
"format": {
"name": "Upload Format",
"name_zh": "上报数据格式",
"description": "JSON format of the data reported in the upload topic. In values mode, data are split into `values` and `errors` sub objects. In tags mode, tag data are put in a single array.",
"description_zh": "上报数据的JSON格式。在values模式下,数据被分为`values`和`errors`两个子对象。在tags模式下,tag数据被放在一个数组中。",
"description": "JSON format of the data reported. In values-mode, data are split into `values` and `errors` sub objects. In tags-mode, tag data are put in a single array.",
"description_zh": "上报数据的 JSON 格式。在 values-format 格式下,数据被分为 `values` 和 `errors` 两个子对象。在 tags-format 格式下,数据被放在一个数组中。",
"attribute": "required",
"type": "map",
"default": 0,
"valid": {
"map": [
{
"key": "format-values",
"key": "values-format",
"value": 0
},
{
"key": "format-tags",
"key": "tags-format",
"value": 1
}
]
}
},
"offline-cache": {
"name": "Offline Data Caching",
"name_zh": "离线缓存",
"description": "Offline caching switch. Cache MQTT messages when offline, and sync cached messages when back online.",
"description_zh": "离线缓存开关。连接断开时缓存 MQTT 消息,连接重建时同步缓存的 MQTT 消息到服务器。",
"attribute": "optional",
"type": "bool",
"default": false,
"valid": {}
},
"cache-mem-size": {
"name": "Cache Memory Size (MB)",
"name_zh": "缓存内存大小(MB)",
"description": "Max in-memory cache size in megabytes when MQTT connection exception occurs. Should be smaller than cache disk size.",
"description_zh": "当MQTT连接异常时,最大的内存缓存大小(单位:MB)。应该小于缓存磁盘大小。",
"description_zh": "当 MQTT 连接异常时,最大的内存缓存大小(单位:MB)。应该小于缓存磁盘大小。",
"type": "int",
"attribute": "required",
"default": 0,
"condition": {
"field": "offline-cache",
"value": true
},
"valid": {
"min": 0,
"min": 1,
"max": 1024
}
},
@@ -62,12 +87,15 @@
"name": "Cache Disk Size (MB)",
"name_zh": "缓存磁盘大小(MB)",
"description": "Max in-disk cache size in megabytes when MQTT connection exception occurs. Should be larger than cache memory size. If nonzero, cache memory size should also be nonzero.",
"description_zh": "当MQTT连接异常时,最大的磁盘缓存大小(单位:MB)。应该大于缓存内存大小。如果不为0,缓存内存大小也不应该为0。",
"description_zh": "当 MQTT 连接异常时,最大的磁盘缓存大小(单位:MB)。应该大于缓存内存大小。如果不为 0,缓存内存大小也应该不为 0。",
"type": "int",
"attribute": "required",
"default": 0,
"condition": {
"field": "offline-cache",
"value": true
},
"valid": {
"min": 0,
"min": 1,
"max": 10240
}
},
@@ -75,7 +103,7 @@
"name": "Broker Host",
"name_zh": "服务器地址",
"description": "MQTT broker host",
"description_zh": "MQTT服务器地址",
"description_zh": "MQTT 服务器地址",
"attribute": "required",
"type": "string",
"default": "broker.emqx.io",
@@ -87,7 +115,7 @@
"name": "Broker Port",
"name_zh": "服务器端口",
"description": "MQTT broker port",
"description_zh": "MQTT服务器端口",
"description_zh": "MQTT 服务器端口",
"attribute": "required",
"type": "int",
"default": 1883,
@@ -99,8 +127,8 @@
"username": {
"name": "Username",
"name_zh": "用户名",
"description": "User name",
"description_zh": "用户名",
"description": "MQTT user name",
"description_zh": "MQTT 用户名",
"attribute": "optional",
"type": "string",
"default": "",
@@ -111,8 +139,8 @@
"password": {
"name": "Password",
"name_zh": "密码",
"description": "Password",
"description_zh": "密码",
"description": "MQTT user password",
"description_zh": "MQTT 用户密码",
"attribute": "optional",
"type": "string",
"default": "",
@@ -124,7 +152,7 @@
"name": "SSL",
"name_zh": "SSL",
"description": "Enable SSL connection",
"description_zh": "是否启用SSL连接",
"description_zh": "是否启用 SSL 连接",
"attribute": "optional",
"type": "bool",
"default": false,
@@ -132,9 +160,9 @@
},
"ca": {
"name": "CA",
"name_zh": "CA证书",
"description": "CA certificate file",
"description_zh": "CA证书文件",
"name_zh": "CA 证书",
"description": "CA certificate which signs the server certificate",
"description_zh": "签发服务器证书的 CA 证书",
"attribute": "required",
"type": "file",
"condition": {
@@ -148,8 +176,8 @@
"cert": {
"name": "Client Cert",
"name_zh": "客户端证书",
"description": "Client x509 certificate file",
"description_zh": "客户端证书文件",
"description": "Client x509 certificate when using two way authentication",
"description_zh": "使用双向认证时,客户端的 x509 证书",
"attribute": "optional",
"type": "file",
"condition": {
@@ -163,8 +191,8 @@
"key": {
"name": "Client Private Key",
"name_zh": "客户端私钥",
"description": "Client private key file",
"description_zh": "客户端私钥文件",
"description": "Client private key when using two way authentication",
"description_zh": "使用双向认证时,客户端的私钥",
"attribute": "optional",
"type": "file",
"condition": {
@@ -177,9 +205,9 @@
},
"keypass": {
"name": "Client Private Key Password",
"name_zh": "私钥密码",
"description": "Client private key password",
"description_zh": "私钥密码",
"name_zh": "客户端私钥密码",
"description": "Client private key password when using two way authentication with an encrypted client private key",
"description_zh": "使用加密的客户端私钥进行双向认证时,客户端私钥的密码",
"attribute": "optional",
"type": "string",
"condition": {
+85 -33
View File
@@ -115,6 +115,79 @@ static inline int parse_ssl_params(neu_plugin_t *plugin, const char *setting,
return 0;
}
static int parse_cache_params(neu_plugin_t *plugin, const char *setting,
neu_json_elem_t *offline_cache,
neu_json_elem_t *cache_mem_size,
neu_json_elem_t *cache_disk_size)
{
int ret = 0;
char *err_param = NULL;
bool flag_present = true; // whether setting has `offline-cache`
// offline-cache flag, optional
ret = neu_parse_param(setting, NULL, 1, offline_cache);
if (0 != ret) {
plog_info(plugin, "setting no offline cache flag");
flag_present = false; // `offline-cache` not present in setting
}
if (flag_present && !offline_cache->v.val_bool) {
// cache explicitly disabled in setting
cache_mem_size->v.val_int = 0;
cache_disk_size->v.val_int = 0;
return 0;
}
// we are here because one of the following:
// 1. `offline-cache` presents to be true in setting
// 2. no `offline-cache` in setting
// we require `cache_mem_size` and `cache_disk_size` to present in both
// cases, especially in case 2 for backward compatibility.
ret = neu_parse_param(setting, &err_param, 2, cache_mem_size,
cache_disk_size);
if (0 != ret) {
plog_error(plugin, "parsing setting fail, key: `%s`", err_param);
free(err_param);
return -1;
}
if (cache_mem_size->v.val_int > 1024 ||
(flag_present && cache_mem_size->v.val_int < 1)) {
plog_error(plugin, "setting invalid cache memory size: %" PRIi64,
cache_mem_size->v.val_int);
return -1;
}
if (cache_disk_size->v.val_int > 10240 ||
(flag_present && cache_disk_size->v.val_int < 1)) {
plog_error(plugin, "setting invalid cache disk size: %" PRIi64,
cache_disk_size->v.val_int);
return -1;
}
if (cache_mem_size->v.val_int > cache_disk_size->v.val_int) {
plog_error(plugin,
"setting cache memory size %" PRIi64
" larger than cache disk size %" PRIi64,
cache_mem_size->v.val_int, cache_disk_size->v.val_int);
return -1;
}
if (0 == cache_mem_size->v.val_int && 0 != cache_disk_size->v.val_int) {
plog_error(plugin,
"setting cache disk size %" PRIi64 " without memory cache",
cache_disk_size->v.val_int);
return -1;
}
if (!flag_present) {
offline_cache->v.val_bool = cache_mem_size->v.val_int > 0;
}
return 0;
}
int mqtt_config_parse(neu_plugin_t *plugin, const char *setting,
mqtt_config_t *config)
{
@@ -130,6 +203,8 @@ int mqtt_config_parse(neu_plugin_t *plugin, const char *setting,
.attribute = NEU_JSON_ATTRIBUTE_OPTIONAL, // for backward compatibility
};
neu_json_elem_t format = { .name = "format", .t = NEU_JSON_INT };
neu_json_elem_t offline_cache = { .name = "offline-cache",
.t = NEU_JSON_BOOL };
neu_json_elem_t cache_mem_size = { .name = "cache-mem-size",
.t = NEU_JSON_INT };
neu_json_elem_t cache_disk_size = { .name = "cache-disk-size",
@@ -149,8 +224,8 @@ int mqtt_config_parse(neu_plugin_t *plugin, const char *setting,
return -1;
}
ret = neu_parse_param(setting, &err_param, 7, &client_id, &format, &qos,
&cache_mem_size, &cache_disk_size, &host, &port);
ret = neu_parse_param(setting, &err_param, 5, &client_id, &format, &qos,
&host, &port);
if (0 != ret) {
plog_error(plugin, "parsing setting fail, key: `%s`", err_param);
goto error;
@@ -176,32 +251,10 @@ int mqtt_config_parse(neu_plugin_t *plugin, const char *setting,
goto error;
}
// cache memory size, required
if (cache_mem_size.v.val_int > 1024) {
plog_error(plugin, "setting invalid cache memory size: %" PRIi64,
cache_mem_size.v.val_int);
goto error;
}
// cache disk size, required
if (cache_disk_size.v.val_int > 10240) {
plog_error(plugin, "setting invalid cache disk size: %" PRIi64,
cache_disk_size.v.val_int);
goto error;
}
if (cache_mem_size.v.val_int > cache_disk_size.v.val_int) {
plog_error(plugin,
"setting cache memory size %" PRIi64
" larger than cache disk size %" PRIi64,
cache_mem_size.v.val_int, cache_disk_size.v.val_int);
goto error;
}
if (0 == cache_mem_size.v.val_int && 0 != cache_disk_size.v.val_int) {
plog_error(plugin,
"setting cache disk size %" PRIi64 " without memory cache",
cache_disk_size.v.val_int);
// offline cache
ret = parse_cache_params(plugin, setting, &offline_cache, &cache_mem_size,
&cache_disk_size);
if (0 != ret) {
goto error;
}
@@ -234,11 +287,10 @@ int mqtt_config_parse(neu_plugin_t *plugin, const char *setting,
goto error;
}
config->client_id = client_id.v.val_str;
config->qos = qos.v.val_int;
config->format = format.v.val_int;
config->cache =
0 != cache_mem_size.v.val_int || 0 != cache_disk_size.v.val_int;
config->client_id = client_id.v.val_str;
config->qos = qos.v.val_int;
config->format = format.v.val_int;
config->cache = offline_cache.v.val_bool;
config->cache_mem_size = cache_mem_size.v.val_int * MB;
config->cache_disk_size = cache_disk_size.v.val_int * MB;
config->host = host.v.val_str;
+29 -82
View File
@@ -42,8 +42,6 @@
#include "log_handle.h"
#include "utils/utarray.h"
UT_array *collect_log_files();
void handle_logs_files(nng_aio *aio)
{
void * data = NULL;
@@ -114,94 +112,43 @@ void handle_log_level(nng_aio *aio)
NEU_PROCESS_HTTP_REQUEST_VALIDATE_JWT(
aio, neu_json_update_log_level_req_t,
neu_json_decode_update_log_level_req, {
char node_name[NEU_NODE_NAME_LEN] = { 0 };
int ret = 0;
neu_reqresp_head_t header = { 0 };
neu_req_update_log_level_t cmd = { 0 };
zlog_category_t * ct = zlog_get_category(req->node_name);
zlog_category_t * neuron = zlog_get_category("neuron");
UT_array *log_files = collect_log_files();
utarray_foreach(log_files, char **, log_file)
{
if (strncmp(*log_file, req->node_name, strlen(*log_file) - 4) ==
0) {
strcpy(node_name, req->node_name);
}
}
if (strlen(node_name) == 0) {
NEU_JSON_RESPONSE_ERROR(NEU_ERR_NODE_NOT_EXIST, {
ret = zlog_level_switch(ct, ZLOG_LEVEL_DEBUG);
if (ret != 0) {
nlog_error("Modify node log level fail, node_name:%s, "
"ret: %d,",
req->node_name, ret);
NEU_JSON_RESPONSE_ERROR(NEU_ERR_EINTERNAL, {
neu_http_response(aio, error_code.error, result_error);
});
} else {
int ret = 0;
neu_reqresp_head_t header = { 0 };
neu_req_update_log_level_t cmd = { 0 };
zlog_category_t * ct = zlog_get_category(node_name);
zlog_category_t * neuron = zlog_get_category("neuron");
ret = zlog_level_switch(ct, ZLOG_LEVEL_DEBUG);
if (ret != 0) {
nlog_error("Modify node log level fail, node_name:%s, "
"ret: %d,",
node_name, ret);
NEU_JSON_RESPONSE_ERROR(NEU_ERR_EINTERNAL, {
neu_http_response(aio, error_code.error, result_error);
});
}
ret = zlog_level_switch(neuron, ZLOG_LEVEL_DEBUG);
if (ret != 0) {
nlog_error("Modify neuron log level fail, ret: %d", ret);
NEU_JSON_RESPONSE_ERROR(NEU_ERR_EINTERNAL, {
neu_http_response(aio, error_code.error, result_error);
});
}
header.ctx = aio;
header.type = NEU_REQ_UPDATE_LOG_LEVEL;
strcpy(cmd.node, req->node_name);
ret = neu_plugin_op(plugin, header, &cmd);
if (ret != 0) {
NEU_JSON_RESPONSE_ERROR(NEU_ERR_IS_BUSY, {
neu_http_response(aio, NEU_ERR_IS_BUSY, result_error);
});
}
}
utarray_free(log_files);
ret = zlog_level_switch(neuron, ZLOG_LEVEL_DEBUG);
if (ret != 0) {
nlog_error("Modify neuron log level fail, ret: %d", ret);
NEU_JSON_RESPONSE_ERROR(NEU_ERR_EINTERNAL, {
neu_http_response(aio, error_code.error, result_error);
});
}
header.ctx = aio;
header.type = NEU_REQ_UPDATE_LOG_LEVEL;
strcpy(cmd.node, req->node_name);
ret = neu_plugin_op(plugin, header, &cmd);
if (ret != 0) {
NEU_JSON_RESPONSE_ERROR(NEU_ERR_IS_BUSY, {
neu_http_response(aio, NEU_ERR_IS_BUSY, result_error);
});
}
})
}
static inline bool ends_with(const char *str, const char *suffix)
{
size_t m = strlen(str);
size_t n = strlen(suffix);
return m >= n && !strcmp(str + m - n, suffix);
}
UT_array *collect_log_files()
{
DIR *dp = opendir("./logs");
if (NULL == dp) {
nlog_error("open logs directory fail");
return NULL;
}
UT_array *log_files = NULL;
utarray_new(log_files, &ut_str_icd);
struct dirent *de = NULL;
while (NULL != (de = readdir(dp))) {
char *name = de->d_name;
if (ends_with(de->d_name, ".log")) {
utarray_push_back(log_files, &name);
}
}
closedir(dp);
return log_files;
}
int read_file(const char *file_name, void **datap, size_t *lenp)
{
int rv = 0;
-8
View File
@@ -224,14 +224,6 @@ static struct {
.plugin = "Modbus TCP",
.schema = "modbus-tcp",
},
{
.plugin = "Modbus TCP community",
.schema = "modbus-tcp-community",
},
{
.plugin = "Modbus TCP QH",
.schema = "modbus-qh-tcp",
},
{
.plugin = "Monitor",
.schema = "monitor",
+5 -1
View File
@@ -205,7 +205,11 @@ ssize_t modbus_s_tcp_req(uint8_t *req, uint16_t req_len, uint8_t *res,
*res_len += sizeof(struct modbus_data);
*res_len += len;
res_data->n_byte = len;
if (len > 0xff) {
res_data->n_byte = 0xff;
} else {
res_data->n_byte = len;
}
res_header->len += sizeof(struct modbus_data) + len;
break;
case MODBUS_WRITE_S_COIL:
+3 -2
View File
@@ -23,7 +23,7 @@
#include <stdlib.h>
#include <string.h>
#include <neuron.h>
#include "neuron.h"
#include "modbus_s.h"
@@ -31,6 +31,7 @@ zlog_category_t *neuron = NULL;
neu_events_t * events = NULL;
neu_event_io_t * tcp_server_event = NULL;
neu_conn_t * conn = NULL;
int64_t global_timestamp = 0;
static void start_listen(void *data, int fd);
static void stop_listen(void *data, int fd);
@@ -222,7 +223,7 @@ static int recv_msg(enum neu_event_io_type type, int fd, void *usr_data)
switch (type) {
case NEU_EVENT_IO_READ: {
ssize_t len = 0;
uint8_t res[60000] = { 0 };
uint8_t res[20000] = { 0 };
int res_len = 0;
len = neu_conn_tcp_server_recv(conn, fd, buf->buf + buf->len,
+7 -2
View File
@@ -302,6 +302,8 @@ void neu_adapter_driver_write_tag(neu_adapter_driver_t *driver,
(double) (cmd->value.value.d64 / tag->decimal);
} else if (cmd->value.type == NEU_TYPE_DOUBLE) {
cmd->value.value.d64 = cmd->value.value.d64 / tag->decimal;
} else if (cmd->value.type == NEU_TYPE_FLOAT) {
cmd->value.value.f32 = cmd->value.value.f32 / tag->decimal;
}
break;
default:
@@ -360,8 +362,11 @@ void neu_adapter_driver_write_tag(neu_adapter_driver_t *driver,
}
break;
case NEU_TYPE_FLOAT:
cmd->value.type = NEU_TYPE_FLOAT;
cmd->value.value.f32 = (float) cmd->value.value.d64;
if (NEU_TYPE_FLOAT != cmd->value.type) {
cmd->value.value.f32 = (float) cmd->value.value.d64;
cmd->value.type = NEU_TYPE_FLOAT;
}
switch (tag->option.value32.endian) {
case NEU_DATATAG_ENDIAN_LB32:
neu_ntohs_p((uint16_t *) cmd->value.value.bytes);
+7
View File
@@ -101,6 +101,11 @@ static inline size_t memory_used()
return parse_memory_fields(3);
}
static inline size_t memory_cache()
{
return parse_memory_fields(6);
}
static inline int disk_usage(size_t *size_p, size_t *used_p, size_t *avail_p)
{
struct statvfs buf = {};
@@ -276,6 +281,7 @@ void neu_metrics_visist(neu_metrics_cb_t cb, void *data)
{
unsigned cpu = cpu_usage();
size_t mem_used = memory_used();
size_t mem_cache = memory_cache();
size_t disk_size = 0, disk_used = 0, disk_avail = 0;
disk_usage(&disk_size, &disk_used, &disk_avail);
bool core_dumped = has_core_dumps();
@@ -284,6 +290,7 @@ void neu_metrics_visist(neu_metrics_cb_t cb, void *data)
g_metrics_.cpu_percent = cpu;
g_metrics_.cpu_cores = get_nprocs();
g_metrics_.mem_used_bytes = mem_used;
g_metrics_.mem_cache_bytes = mem_cache;
g_metrics_.disk_size_gibibytes = disk_size;
g_metrics_.disk_used_gibibytes = disk_used;
g_metrics_.disk_avail_gibibytes = disk_avail;
+62 -11
View File
@@ -69,6 +69,8 @@ struct neu_conn {
static void conn_tcp_server_add_client(neu_conn_t *conn, int fd,
struct sockaddr_in client);
static void conn_tcp_server_del_client(neu_conn_t *conn, int fd);
static int conn_tcp_server_replace_client(neu_conn_t *conn, int fd,
struct sockaddr_in client);
static void conn_tcp_server_listen(neu_conn_t *conn);
static void conn_tcp_server_stop(neu_conn_t *conn);
@@ -180,16 +182,6 @@ int neu_conn_tcp_server_accept(neu_conn_t *conn)
return -1;
}
if (conn->tcp_server.n_client >= conn->param.params.tcp_server.max_link) {
close(fd);
zlog_warn(conn->param.log, "%s:%d accpet max link: %d, reject",
conn->param.params.tcp_server.ip,
conn->param.params.tcp_server.port,
conn->param.params.tcp_server.max_link);
pthread_mutex_unlock(&conn->mtx);
return -1;
}
if (conn->block) {
struct timeval tv = {
.tv_sec = conn->param.params.tcp_server.timeout / 1000,
@@ -199,7 +191,23 @@ int neu_conn_tcp_server_accept(neu_conn_t *conn)
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
}
conn_tcp_server_add_client(conn, fd, client);
if (conn->tcp_server.n_client >= conn->param.params.tcp_server.max_link) {
int free_fd = conn_tcp_server_replace_client(conn, fd, client);
if (free_fd > 0) {
zlog_warn(conn->param.log, "replace old client %d with %d", free_fd,
fd);
} else {
close(fd);
zlog_warn(conn->param.log, "%s:%d accpet max link: %d, reject",
conn->param.params.tcp_server.ip,
conn->param.params.tcp_server.port,
conn->param.params.tcp_server.max_link);
pthread_mutex_unlock(&conn->mtx);
return -1;
}
} else {
conn_tcp_server_add_client(conn, fd, client);
}
conn->is_connected = true;
conn->connected(conn->data, fd);
@@ -728,6 +736,30 @@ static void conn_connect(neu_conn_t *conn)
cfsetospeed(&tty_opt, B2400);
cfsetispeed(&tty_opt, B2400);
break;
case NEU_CONN_TTY_BAUD_1800:
cfsetospeed(&tty_opt, B1800);
cfsetispeed(&tty_opt, B1800);
break;
case NEU_CONN_TTY_BAUD_1200:
cfsetospeed(&tty_opt, B1200);
cfsetispeed(&tty_opt, B1200);
break;
case NEU_CONN_TTY_BAUD_600:
cfsetospeed(&tty_opt, B600);
cfsetispeed(&tty_opt, B600);
break;
case NEU_CONN_TTY_BAUD_300:
cfsetospeed(&tty_opt, B300);
cfsetispeed(&tty_opt, B300);
break;
case NEU_CONN_TTY_BAUD_200:
cfsetospeed(&tty_opt, B200);
cfsetispeed(&tty_opt, B200);
break;
case NEU_CONN_TTY_BAUD_150:
cfsetospeed(&tty_opt, B150);
cfsetispeed(&tty_opt, B150);
break;
}
switch (conn->param.params.tty_client.parity) {
@@ -867,6 +899,25 @@ static void conn_tcp_server_del_client(neu_conn_t *conn, int fd)
}
}
static int conn_tcp_server_replace_client(neu_conn_t *conn, int fd,
struct sockaddr_in client)
{
for (int i = 0; i < conn->param.params.tcp_server.max_link; i++) {
if (conn->tcp_server.clients[i].fd > 0) {
int ret = conn->tcp_server.clients[i].fd;
close(conn->tcp_server.clients[i].fd);
conn->tcp_server.clients[i].fd = 0;
conn->tcp_server.clients[i].fd = fd;
conn->tcp_server.clients[i].client = client;
return ret;
}
}
return 0;
}
int neu_conn_stream_consume(neu_conn_t *conn, void *context,
neu_conn_stream_consume_fn fn)
{
+17
View File
@@ -150,6 +150,7 @@ static void connect_cb(nng_pipe p, nng_pipe_ev ev, void *arg);
static inline task_t *client_alloc_task(neu_mqtt_client_t *client);
static inline void client_free_task(neu_mqtt_client_t *client, task_t *task);
static inline size_t client_task_free_list_len(neu_mqtt_client_t *client);
static inline void client_add_subscription(neu_mqtt_client_t *client,
subscription_t * sub);
static int client_send_sub_msg(neu_mqtt_client_t *client,
@@ -545,6 +546,16 @@ static inline void client_free_task(neu_mqtt_client_t *client, task_t *task)
DL_PREPEND(client->task_free_list, task);
}
static inline size_t client_task_free_list_len(neu_mqtt_client_t *client)
{
size_t count = 0;
task_t *elt = NULL;
// count by iterating the list,
// this won't be a problem as a short free list is expected
DL_COUNT(client->task_free_list, elt, count);
return count;
}
static inline void client_add_subscription(neu_mqtt_client_t *client,
subscription_t * sub)
{
@@ -1149,6 +1160,12 @@ int neu_mqtt_client_close(neu_mqtt_client_t *client)
}
nng_mtx_lock(client->mtx);
// wait for all tasks
while (client_task_free_list_len(client) != client->task_count) {
nng_mtx_unlock(client->mtx);
nng_msleep(100);
nng_mtx_lock(client->mtx);
}
client->open = false;
nng_mtx_unlock(client->mtx);
-3
View File
@@ -105,9 +105,6 @@ int neu_json_decode_global_config_req_tags(
}
req->n_tag = json_array_size(tag_array);
if (req->n_tag <= 0) {
goto decode_fail;
}
req->tags = calloc(req->n_tag, sizeof(*req->tags));
if (NULL == req->tags) {
-3
View File
@@ -311,9 +311,6 @@ int neu_json_decode_get_driver_group_resp_json(
}
resp->n_group = json_array_size(grp_array);
if (resp->n_group <= 0) {
goto decode_fail;
}
resp->groups = calloc(resp->n_group, sizeof(*resp->groups));
if (NULL == resp->groups) {
+1 -1
View File
@@ -47,4 +47,4 @@ gtest_discover_tests(json_test)
gtest_discover_tests(http_test)
gtest_discover_tests(jwt_test)
gtest_discover_tests(base64_test)
gtest_discover_tests(tag_sort_test)
gtest_discover_tests(tag_sort_test)
+1 -1
View File
@@ -1 +1 @@
2.4.0
2.4.3
+1 -1
View File
@@ -8,4 +8,4 @@ simple = "%d [%V] %f:%L %m%n"
[rules]
*.* "./logs/%c.log", 100MB * 10 ~ "./logs/%c.#2s.log"; simple
*.* "./logs/%c.log", 50MB * 1 ~ "./logs/%c.#2s.log"; simple