mirror of
https://github.com/emqx/neuron.git
synced 2026-10-30 07:45:59 +08:00
Compare commits
44
Commits
v2.8-batch
...
2.4.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fed03422b3 | ||
|
|
c8a06bfc30 | ||
|
|
fc12d7fbff | ||
|
|
92f387ae7b | ||
|
|
7cb61f6fa5 | ||
|
|
72eb9c4219 | ||
|
|
faaef6c2e7 | ||
|
|
ff22e3413e | ||
|
|
393d85741a | ||
|
|
680a2839c2 | ||
|
|
5ee46fa972 | ||
|
|
ae94644430 | ||
|
|
f5c730dcfc | ||
|
|
488cccf521 | ||
|
|
a0d5851719 | ||
|
|
c518d6c196 | ||
|
|
c385103c9c | ||
|
|
f216b3f4e9 | ||
|
|
c768f45e9e | ||
|
|
fa55497b55 | ||
|
|
5336d1c250 | ||
|
|
dede8ee8f4 | ||
|
|
17e2015a9c | ||
|
|
3361284252 | ||
|
|
8f6d9d1c41 | ||
|
|
3f75222410 | ||
|
|
ddf5d252f6 | ||
|
|
6e467f5f27 | ||
|
|
fd1266bc9e | ||
|
|
d8b605ef1c | ||
|
|
c9122b5b7c | ||
|
|
8c11cb3466 | ||
|
|
80680c55f5 | ||
|
|
132c25b4ad | ||
|
|
cc7e5d092a | ||
|
|
2e3f6d6db2 | ||
|
|
43a95b7910 | ||
|
|
053be086b0 | ||
|
|
66dba0f69a | ||
|
|
c6752c5761 | ||
|
|
53f5c496ea | ||
|
|
1e28e4bc0e | ||
|
|
2bbcc6a154 | ||
|
|
00d6418288 |
@@ -12,4 +12,3 @@ PointerAlignment: Right
|
||||
SpaceAfterCStyleCast: true
|
||||
AllowShortFunctionsOnASingleLine: Inline
|
||||
BreakBeforeBinaryOperators: None
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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"
|
||||
]
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
@@ -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) {
|
||||
|
||||
@@ -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, ¶m);
|
||||
} else {
|
||||
plugin->common.link_state = NEU_NODE_LINK_STATE_DISCONNECTED;
|
||||
plugin->conn =
|
||||
neu_conn_new(¶m, (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
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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, ¶m);
|
||||
} else {
|
||||
plugin->common.link_state = NEU_NODE_LINK_STATE_DISCONNECTED;
|
||||
plugin->conn =
|
||||
neu_conn_new(¶m, (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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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, ¶m);
|
||||
} else {
|
||||
plugin->common.link_state = NEU_NODE_LINK_STATE_DISCONNECTED;
|
||||
plugin->conn =
|
||||
neu_conn_new(¶m, (void *) plugin, modbus_conn_connected,
|
||||
modbus_conn_disconnected);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user