mirror of
https://github.com/emqx/neuron.git
synced 2026-10-28 14:07:20 +08:00
Merge pull request #1176 from hxy7yx/main
Open source Modbus TCP/RTU, delete existing Modbus TCP community
This commit is contained in:
@@ -12,4 +12,3 @@ PointerAlignment: Right
|
||||
SpaceAfterCStyleCast: true
|
||||
AllowShortFunctionsOnASingleLine: Inline
|
||||
BreakBeforeBinaryOperators: None
|
||||
|
||||
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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