diff --git a/CMakeLists.txt b/CMakeLists.txt index 7fc2d56e..84acda35 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -120,6 +120,7 @@ set(NEURON_BASE_SOURCES src/base/neu_data_expr.c src/base/neu_errcodes.c src/connection/tcp_client.c + src/connection/tcp_server.c src/utils/atomic_data.c src/utils/mem_alloc.c src/utils/idhash.c @@ -151,6 +152,9 @@ add_subdirectory(plugins/modbus) add_subdirectory(plugins/mqtt) add_subdirectory(plugins/opcua) +# define for simulator +add_subdirectory(simulator) + if(NOT DISABLE_UT) # define for tests enable_testing() diff --git a/ft/Keyword/Neuron.py b/ft/Keyword/Neuron.py index 5f5beee1..9386f140 100644 --- a/ft/Keyword/Neuron.py +++ b/ft/Keyword/Neuron.py @@ -87,3 +87,36 @@ class Subscribe(object): ret = 0 break return ret + + +class Read(object): + + def __init__(self): + pass + + def Compare_Tag_Value_Int(self, tags, id, value): + ret = -1 + for tag in tags: + if tag['id'] == int(id): + if tag['value'] == int(value): + ret = 0 + break + return ret + + def Compare_Tag_Value_Float(self, tags, id, value): + ret = -1 + for tag in tags: + if tag['id'] == int(id): + if tag['value'] <= float(value) + 0.1 and tag['value'] >= float(value) - 0.1: + ret = 0 + break + return ret + + def Compare_Tag_Value_String(self, tags, id, value): + ret = -1 + for tag in tags: + if tag['id'] == int(id): + if tag['value'] == str(value): + ret = 0 + break + return ret diff --git a/ft/error.robot b/ft/error.robot index d4460af0..360d2981 100644 --- a/ft/error.robot +++ b/ft/error.robot @@ -1,19 +1,21 @@ *** Variables *** ${ERR_SUCCESS} 0 -${ERR_INTERNAL} -1001 -${ERR_REQUEST_BODY_INVALID} -1002 -${ERR_REQUEST_PARAM_INVALID} -1003 -${ERR_PLUGIN_NAME_NOT_FOUND} -2000 -${ERR_NODE_TYPE_INVALID} -2001 -${ERR_NODE_EXIST} -2002 -${ERR_NODE_NOT_EXIST} -2003 -${ERR_GRPCONFIG_NOT_EXIST} -2004 -${ERR_TAG_NOT_EXIST} -2005 -${ERR_TAG_ATTRIBUTE_NOT_SUPPORT} -2006 -${ERR_NODE_SETTING_INVALID} -2007 -${ERR_NODE_SETTING_NOT_EXIST} -2008 -${ERR_GRP_CONFIG_IN_USE} -2009 -${ERR_NODE_NOT_READY} -2010 -${ERR_NODE_IS_RUNNING} -2011 -${ERR_NODE_NOT_RUNNING} -2012 -${ERR_NODE_IS_STOPED} -2013 +${ERR_INTERNAL} 1001 +${ERR_REQUEST_BODY_INVALID} 1002 +${ERR_REQUEST_PARAM_INVALID} 1003 +${ERR_PLUGIN_NAME_NOT_FOUND} 2000 +${ERR_NODE_TYPE_INVALID} 2001 +${ERR_NODE_EXIST} 2002 +${ERR_NODE_NOT_EXIST} 2003 +${ERR_GRPCONFIG_NOT_EXIST} 2004 +${ERR_TAG_NOT_EXIST} 2005 +${ERR_TAG_ATTRIBUTE_NOT_SUPPORT} 2006 +${ERR_NODE_SETTING_INVALID} 2007 +${ERR_NODE_SETTING_NOT_EXIST} 2008 +${ERR_GRP_CONFIG_IN_USE} 2009 +${ERR_NODE_NOT_READY} 2010 +${ERR_NODE_IS_RUNNING} 2011 +${ERR_NODE_NOT_RUNNING} 2012 +${ERR_NODE_IS_STOPED} 2013 +${ERR_GRP_NOT_SUBSCRIBE} 2014 +${ERR_TAG_TYPE_NOT_SUPPORT} 2015 \ No newline at end of file diff --git a/ft/grp_config_api_test.robot b/ft/grp_config_api_test.robot index 46f53f49..b518276f 100644 --- a/ft/grp_config_api_test.robot +++ b/ft/grp_config_api_test.robot @@ -161,7 +161,6 @@ Delete the subscribe group config, it should return failure North APP unsubscribe non-existent group config, it should return failure ${app_node_id} Get Node ID ${NODE_APP} ${app_node} ${driver_node_id} Get Node ID ${NODE_DRIVER} ${driver_node} - #Add Group Config ${driver_node_id} grp_config DELETE /api/v2/subscribe {"src_node_id": ${driver_node_id}, "dst_node_id": ${app_node_id}, "name": "xxgrp_config"} diff --git a/ft/modbus_tcp_test.robot b/ft/modbus_tcp_test.robot index 79e340a7..5ca7e2a6 100644 --- a/ft/modbus_tcp_test.robot +++ b/ft/modbus_tcp_test.robot @@ -1,40 +1,185 @@ *** Settings *** +Library Keyword.Neuron.Neuron +Library Keyword.Neuron.Tool +Library Keyword.Neuron.Node +Library Keyword.Neuron.GrpConfig +Library Keyword.Neuron.Subscribe +Library Keyword.Neuron.Tag +Resource error.robot +Resource neuron.robot +Resource simulator.robot +Library REST http://127.0.0.1:7001 +Suite Setup Neuron Context Ready +Suite Teardown Stop All Processes *** Variables *** +${node_id} +${group} config_modbus_tcp_sample_2 *** Test Cases *** -#Read unsubscribed point, it should return failure -#Read a point in the coil area, and the data as bit type, it should return success -#Read a point in the input area, and the data as bit type, it should return success +Read unsubscribed point, it should return failure + Add Tags ${node_id} xx {"name": "tag1", "address": "1!40001", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_DATA_TYPE_INT16}} + + Sleep 3s + + POST /api/v2/read {"node_id": ${node_id}, "group_config_name": "xx"} + + Integer response status 404 + Integer $.error ${ERR_GRP_NOT_SUBSCRIBE} + + [Teardown] Del Group Config ${node_id} xx + +Read a point in the hlod reg area, and the data as int16, it should return success + Add Tags ${node_id} ${group} {"name": "tag1", "address": "1!40001", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_DATA_TYPE_INT16}} + + ${tag_id} = Get Tag ID ${node_id} ${group} tag1 + should not be equal as integers ${tag_id} -1 + Sleep 3s + + ${tags} = Read Tags ${node_id} ${group} + Compare Tag Value As Int ${tags}.tags ${tag_id} 0 + + Write Tags ${node_id} ${group} {"id": ${tag_id}, "value": 123} + + sleep 3s + + ${tags} = Read Tags ${node_id} ${group} + Compare Tag Value As Int ${tags}.tags ${tag_id} 123 + + [Teardown] Del Tags ${node_id} ${group} ${tag_id} + +Read a point in the hold reg area, and the data as uint16, it should return success + Add Tags ${node_id} ${group} {"name": "tag1", "address": "1!40002", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_DATA_TYPE_UINT16}} + + ${tag_id} = Get Tag ID ${node_id} ${group} tag1 + should not be equal as integers ${tag_id} -1 + Sleep 3s + + ${tags} = Read Tags ${node_id} ${group} + Compare Tag Value As Int ${tags}.tags ${tag_id} 0 + + Write Tags ${node_id} ${group} {"id": ${tag_id}, "value": 123} + + sleep 3s + + ${tags} = Read Tags ${node_id} ${group} + Compare Tag Value As Int ${tags}.tags ${tag_id} 123 + + [Teardown] Del Tags ${node_id} ${group} ${tag_id} + +Read a point in the hold reg area, and the data as int32, it should return success + Add Tags ${node_id} ${group} {"name": "tag1", "address": "1!40003", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_DATA_TYPE_INT32}} + + ${tag_id} = Get Tag ID ${node_id} ${group} tag1 + should not be equal as integers ${tag_id} -1 + Sleep 3s + + ${tags} = Read Tags ${node_id} ${group} + Compare Tag Value As Int ${tags}.tags ${tag_id} 0 + + Write Tags ${node_id} ${group} {"id": ${tag_id}, "value": 66778899} + + sleep 3s + + ${tags} = Read Tags ${node_id} ${group} + Compare Tag Value As Int ${tags}.tags ${tag_id} 66778899 + + [Teardown] Del Tags ${node_id} ${group} ${tag_id} + +Read a point in the hold reg area, and the data as uint32, it should return success + Add Tags ${node_id} ${group} {"name": "tag1", "address": "1!40005", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_DATA_TYPE_UINT32}} + + ${tag_id} = Get Tag ID ${node_id} ${group} tag1 + should not be equal as integers ${tag_id} -1 + Sleep 3s + + ${tags} = Read Tags ${node_id} ${group} + Compare Tag Value As Int ${tags}.tags ${tag_id} 0 + + Write Tags ${node_id} ${group} {"id": ${tag_id}, "value": 667788995} + + sleep 3s + + ${tags} = Read Tags ${node_id} ${group} + Compare Tag Value As Int ${tags}.tags ${tag_id} 667788995 + + [Teardown] Del Tags ${node_id} ${group} ${tag_id} +Read a point in the hold reg area, and the data as float, it should return success + Add Tags ${node_id} ${group} {"name": "tag1", "address": "1!40007", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_DATA_TYPE_FLOAT}} + + ${tag_id} = Get Tag ID ${node_id} ${group} tag1 + should not be equal as integers ${tag_id} -1 + Sleep 3s + + ${tags} = Read Tags ${node_id} ${group} + Compare Tag Value As Float ${tags}.tags ${tag_id} 0 + + Write Tags ${node_id} ${group} {"id": ${tag_id}, "value": 11.22} + + sleep 3s + + ${tags} = Read Tags ${node_id} ${group} + Compare Tag Value As Float ${tags}.tags ${tag_id} 11.22 + + [Teardown] Del Tags ${node_id} ${group} ${tag_id} + +Read a point in the coil area, and the data as bit type, it should return success + Add Tags ${node_id} ${group} {"name": "tag1", "address": "1!00001", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_DATA_TYPE_BIT}} + + ${tag_id} = Get Tag ID ${node_id} ${group} tag1 + should not be equal as integers ${tag_id} -1 + Sleep 3s + + ${tags} = Read Tags ${node_id} ${group} + Compare Tag Value As Int ${tags}.tags ${tag_id} 0 + + Write Tags ${node_id} ${group} {"id": ${tag_id}, "value": 1} + + sleep 3s + + ${tags} = Read Tags ${node_id} ${group} + Compare Tag Value As Int ${tags}.tags ${tag_id} 1 + + [Teardown] Del Tags ${node_id} ${group} ${tag_id} + +Read a point in the input area, and the data as bit type, it should return success + Add Tags ${node_id} ${group} {"name": "tag1", "address": "1!10001", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_DATA_TYPE_BIT}} + Add Tags ${node_id} ${group} {"name": "tag2", "address": "1!10002", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_DATA_TYPE_BIT}} + + ${tag1_id} = Get Tag ID ${node_id} ${group} tag1 + ${tag2_id} = Get Tag ID ${node_id} ${group} tag2 + should not be equal as integers ${tag1_id} -1 + should not be equal as integers ${tag2_id} -1 + Sleep 4s + + ${tags} = Read Tags ${node_id} ${group} + Compare Tag Value As Int ${tags}.tags ${tag1_id} 0 + Compare Tag Value As Int ${tags}.tags ${tag2_id} 1 + + [Teardown] Del Tags ${node_id} ${group} ${tag1_id},${tag2_id} #Read a point in the input reg area, and the data as int16, it should return success #Read a point in the input reg area, and the data as uint16, it should return success #Read a point in the input reg area, and the data as uint32, it should return success #Read a point in the input reg area, and the data as int32, it should return success #Read a point in the input reg area, and the data as float, it should return success -#Read a point in the hlod reg area, and the data as int16, it should return success -#Read a point in the hlod reg area, and the data as uint16, it should return success -#Read a point in the hlod reg area, and the data as int32, it should return success -#Read a point in the hlod reg area, and the data as uint32, it should return success -#Read a point in the hlod reg area, and the data as float, it should return success #Read multiple points in the coil area, the point address includes continuous and non-continuous, and the data as bit type, it should return success #Read multiple points in the input area, the point address includes continuous and non-continuous, and the data as bit type, it should return success #Read multiple points in the input reg area, the point address includes continuous and non-continuous, and the data includes int16/uint16/int32/uint32/float type, it should return success #Read multiple points in the hold reg area, the point address includes continuous and non-continuous, and the data includes int16/uint16/int32/uint32/float type, it should return success #Read multiple points belonging to different areas(coil/input/input reg/hold reg), the point address includes continuous and non-continuous, and the data include int16/uint16/int32/uint32/float type, it should return success #Read multiple points belonging to different areas(coil/input/input reg/hold reg) and different sites, the point address includes continuous and non-continuous, and the data include int16/uint16/int32/uint32/float type, it should return success -#Write a point in the coil area, and the data as bit type, it should return success -#Write a point in the input area, and the data as bit type, it should return success -#Write a point in the input reg area, and the data as int16, it should return success -#Write a point in the input reg area, and the data as uint16, it should return success -#Write a point in the input reg area, and the data as uint32, it should return success -#Write a point in the input reg area, and the data as int32, it should return success -#Write a point in the input reg area, and the data as float, it should return success -#Write a point in the hlod reg area, and the data as int16, it should return success -#Write a point in the hlod reg area, and the data as uint16, it should return success -#Write a point in the hlod reg area, and the data as int32, it should return success -#Write a point in the hlod reg area, and the data as uint32, it should return success -#Write a point in the hlod reg area, and the data as float, it should return success *** Keywords *** Neuron Context Ready - Neuron Ready \ No newline at end of file + Start Simulator ${MODBUS_TCP_SERVER_SIMULATOR} ${SIMULATOR_DIR} + Neuron Ready + ${id} = Get Node ID ${NODE_DRIVER} modbus-tcp-adapter + + Set Global Variable ${node_id} ${id} + + Node Setting ${node_id} ${MODBUS_TCP_CONFIG} + +Stop All Processes + Stop Neuron + sleep 1s + Terminate All Processes kill=false \ No newline at end of file diff --git a/ft/neuron.robot b/ft/neuron.robot index 01887b85..5c327b1f 100644 --- a/ft/neuron.robot +++ b/ft/neuron.robot @@ -1,3 +1,6 @@ +*** Settings *** +Library Keyword.Neuron.Read + *** Variables *** ${NODE_DRIVER} 1 ${NODE_WEB} 2 @@ -18,6 +21,26 @@ ${NODE_LINK_STATE_DISCONNECTED} 0 ${NODE_LINK_STATE_CONNECTING} 1 ${NODE_LINK_STATE_CONNECTED} 2 +${TAG_ATTRIBUTE_READ} 1 +${TAG_ATTRIBUTE_WRITE} 2 +${TAG_ATTRIBUTE_SUBSCRIBE} 4 +${TAG_ATTRIBUTE_RW} 3 + +${TAG_DATA_TYPE_BYTE} 2 +${TAG_DATA_TYPE_INT8} 3 +${TAG_DATA_TYPE_INT16} 4 +${TAG_DATA_TYPE_INT32} 5 +${TAG_DATA_TYPE_INT64} 6 +${TAG_DATA_TYPE_UINT8} 7 +${TAG_DATA_TYPE_UINT16} 8 +${TAG_DATA_TYPE_UINT32} 9 +${TAG_DATA_TYPE_UINT64} 10 +${TAG_DATA_TYPE_FLOAT} 11 +${TAG_DATA_TYPE_DOUBLE} 12 +${TAG_DATA_TYPE_BOOL} 13 +${TAG_DATA_TYPE_BIT} 14 +${TAG_DATA_TYPE_STRING} 15 + ${PLUGIN_MODBUS_TCP} modbus-tcp-plugin ${PLUGIN_MQTT} mqtt-plugin @@ -26,7 +49,14 @@ ${MQTT_CONFIG_REQ_TOPIC} test/mqtt_req ${MQTT_CONFIG_RES_TOPIC} test/mqtt_res ${MQTT_CONFIG} {"req-topic": "${MQTT_CONFIG_REQ_TOPIC}", "res-topic": "${MQTT_CONFIG_RES_TOPIC}", "ssl": false, "host": "${MQTT_CONFIG_HOST}", "port": 1883, "username": "", "password": "", "ca-path":"", "ca-file": ""} +${MODBUS_TCP_CONFIG} {"host": "127.0.0.1", "port": 60502, "connection_mode": 0} + *** Keywords *** +To Array + [Arguments] ${str_array} + ${tmp} Array ${str_array} + [RETURN] ${tmp}[0] + Neuron Ready Start Neuron Sleep 3 @@ -62,7 +92,7 @@ Del Node Node Setting [Arguments] ${node_id} ${config} - POST /api/v2/node/setting ${config} + POST /api/v2/node/setting {"node_id": ${node_id}, "params": ${config}} Integer response status 200 Get Node State @@ -104,7 +134,73 @@ Subscribe Group Integer response status 200 Integer response body error ${ERR_SUCCESS} -To Array - [Arguments] ${str_array} - ${tmp} Array ${str_array} - [RETURN] ${tmp}[0] +Add Tags + [Arguments] ${node_id} ${group} ${tags} + + POST /api/v2/tags {"node_id": ${node_id}, "group_config_name": "${group}", "tags": [${tags}]} + + Integer response status 200 + +Del Tags + [Arguments] ${node_id} ${group} ${ids} + + DELETE /api/v2/tags {"node_id": ${node_id}, "group_config_name": "${group}", "ids": [${ids}]} + + Integer response status 200 + +Get Tag ID + [Arguments] ${node_id} ${group} ${tag_name} + + GET /api/v2/tags?node_id=${node_id}&group_config_name=${group} + + Integer response status 200 + + ${result} = To Array $.tags + + ${id} = Tag Find By Name ${result} ${tag_name} + [Return] ${id} + +Read Tags + [Arguments] ${node_id} ${group} + + POST /api/v2/read {"node_id": ${node_id}, "group_config_name": "${group}"} + + Integer response status 200 + output $ + [Return] $ + +Write Tags + [Arguments] ${node_id} ${group} ${tags} + + POST /api/v2/write {"node_id": ${node_id}, "group_config_name": "${group}", "tags":[${tags}]} + + Integer response status 200 + +Compare Tag Value As Int + [Arguments] ${tags} ${id} ${value} + + ${val} = To Array ${tags} + ${ret} = Compare Tag Value Int ${val} ${id} ${value} + + should be equal as integers ${ret} 0 + +Compare Tag Value As Float + [Arguments] ${tags} ${id} ${value} + + ${val} = To Array ${tags} + ${ret} = Compare Tag Value Float ${val} ${id} ${value} + + should be equal as integers ${ret} 0 + +Compare Tag Value As String + [Arguments] ${tags} ${id} ${value} + + ${val} = To Array ${tags} + ${ret} = Compare Tag Value String ${val} ${id} ${value} + + should be equal as integers ${ret} 0 + + + + + diff --git a/ft/node_api_test.robot b/ft/node_api_test.robot index c7f1a438..219411c8 100644 --- a/ft/node_api_test.robot +++ b/ft/node_api_test.robot @@ -214,12 +214,12 @@ Start the configured node, it should return success Sleep 1s - Node Setting ${modbus_node_id} {"node_id": ${modbus_node_id},"params": {"host": "127.0.0.1", "port": 502, "connection_mode": 0}} + Node Setting ${modbus_node_id} {"host": "127.0.0.1", "port": 502, "connection_mode": 0} ${running_state} ${link_state} Get Node State ${modbus_node_id} should be equal as integers ${running_state} ${NODE_STATE_READY} - should be equal as integers ${link_state} ${NODE_LINK_STATE_DISCONNECTED} + should be equal as integers ${link_state} ${NODE_LINK_STATE_CONNECTING} POST /api/v2/node/ctl {"id": ${modbus_node_id}, "cmd": ${NODE_CTL_START}} @@ -229,7 +229,7 @@ Start the configured node, it should return success ${running_state} ${link_state} Get Node State ${modbus_node_id} should be equal as integers ${running_state} ${NODE_STATE_RUNNING} - should be equal as integers ${link_state} ${NODE_LINK_STATE_DISCONNECTED} + should be equal as integers ${link_state} ${NODE_LINK_STATE_CONNECTING} Start the stopped node, it should return success @@ -240,7 +240,7 @@ Start the stopped node, it should return success ${running_state} ${link_state} Get Node State ${modbus_node_id} should be equal as integers ${running_state} ${NODE_STATE_STOP} - should be equal as integers ${link_state} ${NODE_LINK_STATE_DISCONNECTED} + should be equal as integers ${link_state} ${NODE_LINK_STATE_CONNECTING} POST /api/v2/node/ctl {"id": ${modbus_node_id}, "cmd": ${NODE_CTL_START}} @@ -249,7 +249,7 @@ Start the stopped node, it should return success ${running_state} ${link_state} Get Node State ${modbus_node_id} should be equal as integers ${running_state} ${NODE_STATE_RUNNING} - should be equal as integers ${link_state} ${NODE_LINK_STATE_DISCONNECTED} + should be equal as integers ${link_state} ${NODE_LINK_STATE_CONNECTING} Start the running node, it should return failure ${modbus_node_id} Get Node ID ${NODE_DRIVER} modbus-test-node @@ -281,7 +281,7 @@ Stop node that is ready(init/idle), it should return failure Integer response status 409 Integer $.error ${ERR_NODE_NOT_RUNNING} - Node Setting ${modbus_node_id} {"node_id": ${modbus_node_id},"params": {"host": "127.0.0.1", "port": 502, "connection_mode": 0}} + Node Setting ${modbus_node_id} {"host": "127.0.0.1", "port": 502, "connection_mode": 0} POST /api/v2/node/ctl {"id": ${modbus_node_id}, "cmd": ${NODE_CTL_STOP}} @@ -295,30 +295,30 @@ When setting up a READY/RUNNING/STOPED node, the node status will not change Sleep 1s - Node Setting ${modbus_node_id} {"node_id": ${modbus_node_id},"params": {"host": "127.0.0.1", "port": 502, "connection_mode": 0}} + Node Setting ${modbus_node_id} {"host": "127.0.0.1", "port": 502, "connection_mode": 0} ${running_state} ${link_state} Get Node State ${modbus_node_id} should be equal as integers ${running_state} ${NODE_STATE_READY} - should be equal as integers ${link_state} ${NODE_LINK_STATE_DISCONNECTED} + should be equal as integers ${link_state} ${NODE_LINK_STATE_CONNECTING} - Node Setting ${modbus_node_id} {"node_id": ${modbus_node_id},"params": {"host": "127.0.0.1", "port": 502, "connection_mode": 0}} + Node Setting ${modbus_node_id} {"host": "127.0.0.1", "port": 502, "connection_mode": 0} ${running_state} ${link_state} Get Node State ${modbus_node_id} should be equal as integers ${running_state} ${NODE_STATE_READY} - should be equal as integers ${link_state} ${NODE_LINK_STATE_DISCONNECTED} + should be equal as integers ${link_state} ${NODE_LINK_STATE_CONNECTING} Node Ctl ${modbus_node_id} ${NODE_CTL_START} ${running_state} ${link_state} Get Node State ${modbus_node_id} should be equal as integers ${running_state} ${NODE_STATE_RUNNING} - should be equal as integers ${link_state} ${NODE_LINK_STATE_DISCONNECTED} + should be equal as integers ${link_state} ${NODE_LINK_STATE_CONNECTING} Node Ctl ${modbus_node_id} ${NODE_CTL_STOP} ${running_state} ${link_state} Get Node State ${modbus_node_id} should be equal as integers ${running_state} ${NODE_STATE_STOP} - should be equal as integers ${link_state} ${NODE_LINK_STATE_DISCONNECTED} \ No newline at end of file + should be equal as integers ${link_state} ${NODE_LINK_STATE_CONNECTING} \ No newline at end of file diff --git a/ft/simulator.robot b/ft/simulator.robot new file mode 100644 index 00000000..42727eef --- /dev/null +++ b/ft/simulator.robot @@ -0,0 +1,18 @@ +*** Settings *** +Library Process + +*** Variables *** +${SIMULATOR_DIR} build/simulator +${MODBUS_TCP_SERVER_SIMULATOR} ./modbus_tcp_server_simulator + +*** Keywords *** +Start Simulator + [Arguments] ${simulator} ${cwd} + + ${handle} = Start Process ${simulator} cwd=${cwd} + [Return] ${handle} + +Stop Simulator + [Arguments] ${handle} + + Terminate Process ${handle} kill=true \ No newline at end of file diff --git a/ft/tag_api_test.robot b/ft/tag_api_test.robot index b871ab0b..ed6e2f42 100644 --- a/ft/tag_api_test.robot +++ b/ft/tag_api_test.robot @@ -15,7 +15,7 @@ ${test_gconfig} test_grp_config *** Test Cases *** Add different types of tags to the group, it should return success - ${test_node_id} Get Node ID ${NODE_DRIVER} ${test_node} + ${test_node_id} = Get Node ID ${NODE_DRIVER} ${test_node} should not be equal as integers ${test_node_id} 0 @@ -25,7 +25,7 @@ Add different types of tags to the group, it should return success Integer response status 200 - Del Group Config ${test_node_id} ${test_gconfig} + [Teardown] Del Group Config ${test_node_id} ${test_gconfig} Add tags to the non-existent group, the group will be created automatically ${test_node_id} Get Node ID ${NODE_DRIVER} ${test_node} @@ -96,15 +96,16 @@ After adding the tag, query the tag, it should return the added tag Integer response status 200 GET /api/v2/tags?node_id=${test_node_id} - Integer response status 200 - ${result} To Array $.tags + + Integer response status 200 + ${result} To Array $.tags - ${tags_len} Array Len ${result} - should be equal as integers ${tags_len} 2 + ${tags_len} = Array Len ${result} + should be equal as integers ${tags_len} 2 - ${ret} Tag Check ${result} tag1 4 test_grp_config 1 1!400001 - should be equal as integers ${ret} 0 + ${ret} = Tag Check ${result} tag1 4 test_grp_config 1 1!400001 + should be equal as integers ${ret} 0 ${ret} Tag Check ${result} tag2 14 test_grp_config 3 1!00001 should be equal as integers ${ret} 0 diff --git a/include/connection/neu_tcp.h b/include/connection/neu_tcp.h index 1ecad05c..21de45d0 100644 --- a/include/connection/neu_tcp.h +++ b/include/connection/neu_tcp.h @@ -17,12 +17,31 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ +#include #include +typedef ssize_t (*neu_tcp_asy_rcv_cb)(char *recv_buf, ssize_t recv_len); + typedef struct neu_tcp_client neu_tcp_client_t; -neu_tcp_client_t *neu_tcp_client_create(char *server, uint16_t port); +neu_tcp_client_t *neu_tcp_client_create(neu_tcp_client_t *client, char *server, + uint16_t port); void neu_tcp_client_close(neu_tcp_client_t *client); +bool neu_tcp_client_is_connected(neu_tcp_client_t *client); + ssize_t neu_tcp_client_send_recv(neu_tcp_client_t *client, char *send_buf, ssize_t send_len, char *recv_buf, - ssize_t recv_len); \ No newline at end of file + ssize_t recv_len); + +typedef struct neu_tcp_server neu_tcp_server_t; + +neu_tcp_server_t *neu_tcp_server_create(char *local_host, uint16_t local_port); +void neu_tcp_server_close(neu_tcp_server_t *server); +int neu_tcp_server_wait_client(neu_tcp_server_t *server); +ssize_t neu_tcp_server_send_recv(neu_tcp_server_t *server, char *send_buf, + ssize_t send_len, char *recv_buf, + ssize_t recv_len); +ssize_t neu_tcp_server_recv(neu_tcp_server_t *server, char *recv_buf, + ssize_t buf_len, ssize_t recv_len); +ssize_t neu_tcp_server_send(neu_tcp_server_t *server, char *send_buf, + ssize_t send_len); diff --git a/include/neu_errcodes.h b/include/neu_errcodes.h index 68c690d8..69bac41c 100644 --- a/include/neu_errcodes.h +++ b/include/neu_errcodes.h @@ -59,25 +59,28 @@ typedef enum { NEU_ERR_EBADTYPE = -30, NEU_ERR_ECONNSHUT = -31, - NEU_ERR_FAILURE = -1000, // A general error - NEU_ERR_EINTERNAL = -1001, // A neuron interanl error - NEU_ERR_BODY_IS_WRONG = -1002, - NEU_ERR_PARAM_IS_WRONG = -1003, + NEU_ERR_FAILURE = 1000, // A general error + NEU_ERR_EINTERNAL = 1001, // A neuron interanl error + NEU_ERR_BODY_IS_WRONG = 1002, + NEU_ERR_PARAM_IS_WRONG = 1003, - NEU_ERR_PLUGIN_NAME_NOT_FOUND = -2000, - NEU_ERR_NODE_TYPE_INVALID = -2001, - NEU_ERR_NODE_EXIST = -2002, - NEU_ERR_NODE_NOT_EXIST = -2003, - NEU_ERR_GRP_CONFIG_NOT_EXIST = -2004, - NEU_ERR_TAG_NOT_EXIST = -2005, - NEU_ERR_TAG_ATTRIBUTE_NOT_SUPPORT = -2006, - NEU_ERR_NODE_SETTING_INVALID = -2007, - NEU_ERR_NODE_SETTING_NOT_FOUND = -2008, - NEU_ERR_GRP_CONFIG_IN_USE = -2009, - NEU_ERR_NODE_NOT_READY = -2010, - NEU_ERR_NODE_IS_RUNNING = -2011, - NEU_ERR_NODE_NOT_RUNNING = -2012, - NEU_ERR_NODE_IS_STOPED = -2013, + NEU_ERR_PLUGIN_NAME_NOT_FOUND = 2000, + NEU_ERR_NODE_TYPE_INVALID = 2001, + NEU_ERR_NODE_EXIST = 2002, + NEU_ERR_NODE_NOT_EXIST = 2003, + NEU_ERR_GRP_CONFIG_NOT_EXIST = 2004, + NEU_ERR_TAG_NOT_EXIST = 2005, + NEU_ERR_TAG_ATTRIBUTE_NOT_SUPPORT = 2006, + NEU_ERR_NODE_SETTING_INVALID = 2007, + NEU_ERR_NODE_SETTING_NOT_FOUND = 2008, + NEU_ERR_GRP_CONFIG_IN_USE = 2009, + NEU_ERR_NODE_NOT_READY = 2010, + NEU_ERR_NODE_IS_RUNNING = 2011, + NEU_ERR_NODE_NOT_RUNNING = 2012, + NEU_ERR_NODE_IS_STOPED = 2013, + NEU_ERR_GRP_NOT_SUBSCRIBE = 2014, + NEU_ERR_TAG_TYPE_NOT_SUPPORT = 2015, + NEU_ERR_GRP_CONFIG_EXIST = 2016, } neu_err_code_e; #ifdef __cplusplus diff --git a/plugins/modbus/modbus_point.c b/plugins/modbus/modbus_point.c index e7f864be..6a4fb7e4 100644 --- a/plugins/modbus/modbus_point.c +++ b/plugins/modbus/modbus_point.c @@ -109,10 +109,12 @@ int modbus_point_add(modbus_point_context_t *ctx, char *addr, point->value.type = type; insert_point(ctx, point); } else { + log_error("modbus parse addr: %s error", addr); free(point); return -1; } + log_info("add tag: %s success", addr); return 0; } @@ -264,44 +266,40 @@ int modbus_point_all_read(modbus_point_context_t *ctx, bool with_head, ssize_t recv_len = 0; int result = 0; int ret; + (void) with_head; pthread_mutex_lock(&ctx->mtx); - if (with_head) { - for (int i = 0; i < ctx->n_cmd; i++) { - send_len = modbus_read_req_with_head( - send_buf, ctx->cmds[i].id, ctx->cmds[i].device, - ctx->cmds[i].function, ctx->cmds[i].start_addr, - ctx->cmds[i].n_reg); + for (int i = 0; i < ctx->n_cmd; i++) { + send_len = modbus_read_req_with_head( + send_buf, ctx->cmds[i].id, ctx->cmds[i].device, + ctx->cmds[i].function, ctx->cmds[i].start_addr, ctx->cmds[i].n_reg); - recv_len = callback(ctx->arg, send_buf, send_len, recv_buf, - sizeof(recv_buf)); - if (recv_len <= 0) { - log_error("cmd trans fail, id: %hd, function: %hd, addr: %hd, " + recv_len = + callback(ctx->arg, send_buf, send_len, recv_buf, sizeof(recv_buf)); + if (recv_len <= 0) { + log_error("cmd trans fail, id: %hd, function: %hd, addr: %hd, " + "n_reg: %hd", + ctx->cmds[i].id, ctx->cmds[i].function, + ctx->cmds[i].start_addr, ctx->cmds[i].n_reg); + result = -1; + } else { + log_debug("cmd trans success, id: %hd, function: %hd, addr: %hd, " + "n_reg: %hd", + ctx->cmds[i].id, ctx->cmds[i].function, + ctx->cmds[i].start_addr, ctx->cmds[i].n_reg); + ret = process_read_res(&ctx->cmds[i], recv_buf, recv_len); + if (ret != 0) { + result = -1; + log_error("cmd parse error, id: %hd, function: %hd, addr: %hd, " "n_reg: %hd", ctx->cmds[i].id, ctx->cmds[i].function, ctx->cmds[i].start_addr, ctx->cmds[i].n_reg); - result = -1; - } else { - log_debug( - "cmd trans success, id: %hd, function: %hd, addr: %hd, " - "n_reg: %hd", - ctx->cmds[i].id, ctx->cmds[i].function, - ctx->cmds[i].start_addr, ctx->cmds[i].n_reg); - ret = process_read_res(&ctx->cmds[i], recv_buf, recv_len); - if (ret != 0) { - result = -1; - log_error( - "cmd parse error, id: %hd, function: %hd, addr: %hd, " - "n_reg: %hd", - ctx->cmds[i].id, ctx->cmds[i].function, - ctx->cmds[i].start_addr, ctx->cmds[i].n_reg); - } } } - - pthread_mutex_unlock(&ctx->mtx); } + pthread_mutex_unlock(&ctx->mtx); + return result; } diff --git a/plugins/modbus/modbus_tcp.c b/plugins/modbus/modbus_tcp.c index 9da56378..30d4a2da 100644 --- a/plugins/modbus/modbus_tcp.c +++ b/plugins/modbus/modbus_tcp.c @@ -69,6 +69,12 @@ static ssize_t send_recv_callback(void *arg, char *send_buf, ssize_t send_len, char *recv_buf, ssize_t recv_len) { neu_plugin_t *plugin = (neu_plugin_t *) arg; + + if (neu_tcp_client_is_connected(plugin->client)) { + plugin->common.link_state = NEU_PLUGIN_LINK_STATE_CONNECTED; + } else { + plugin->common.link_state = NEU_PLUGIN_LINK_STATE_CONNECTING; + } return neu_tcp_client_send_recv(plugin->client, send_buf, send_len, recv_buf, recv_len); } @@ -164,9 +170,9 @@ setup_read_resp_data_value(neu_datatag_table_t * tag_table, vector_t * ids = neu_taggrp_cfg_get_datatag_ids(grp_config); neu_fixed_array_t *array_resp = NULL; int index = 1; - int resp_res = 0; neu_data_val_t * val_error_resp = neu_dvalue_new(NEU_DTYPE_ERRORCODE); neu_int_val_t int_val_resp = { 0 }; + neu_err_code_e error = NEU_ERR_SUCCESS; read_count = neu_plugin_tag_count_by_attribute(grp_config, tag_table, NEU_ATTRIBUTE_READ); @@ -219,7 +225,7 @@ setup_read_resp_data_value(neu_datatag_table_t * tag_table, neu_int_val_init(&int_val, tag->id, val_error); neu_fixed_array_set(array_resp, index, (void *) &int_val); - resp_res = -1; + error = NEU_ERR_TAG_NOT_EXIST; } else { switch (data_tags.type) { case MODBUS_B8: { @@ -291,11 +297,7 @@ setup_read_resp_data_value(neu_datatag_table_t * tag_table, index += 1; } - if (resp_res == -1) { - neu_dvalue_set_errorcode(val_error_resp, resp_res); - } else { - neu_dvalue_set_errorcode(val_error_resp, 0); - } + neu_dvalue_set_errorcode(val_error_resp, error); neu_int_val_init(&int_val_resp, 0, val_error_resp); neu_fixed_array_set(array_resp, 0, (void *) &int_val_resp); @@ -310,9 +312,9 @@ static neu_data_val_t *setup_write_resp_data_value(neu_data_val_t *write_val, neu_fixed_array_t *array_req = NULL; neu_fixed_array_t *array_resp = NULL; neu_data_val_t * resp_val = NULL; - int resp_res = 0; neu_data_val_t * val_error_resp = neu_dvalue_new(NEU_DTYPE_ERRORCODE); neu_int_val_t int_val_resp = { 0 }; + neu_err_code_e error = NEU_ERR_SUCCESS; neu_dvalue_get_ref_array(write_val, &array_req); @@ -332,7 +334,6 @@ static neu_data_val_t *setup_write_resp_data_value(neu_data_val_t *write_val, } for (uint32_t i = 0; i < array_req->length; i++) { - int ret = 0; neu_int_val_t *int_val = neu_fixed_array_get(array_req, i); neu_dtype_e val_type = neu_value_type_in_dtype(neu_dvalue_get_type(int_val->val)); @@ -352,24 +353,24 @@ static neu_data_val_t *setup_write_resp_data_value(neu_data_val_t *write_val, case NEU_DTYPE_INT16: data.type = MODBUS_B16; data.val.val_u16 = (uint16_t) i64; - ret = modbus_point_write(tag->addr_str, &data, - send_recv_callback, plugin); + error = modbus_point_write(tag->addr_str, &data, + send_recv_callback, plugin); break; case NEU_DTYPE_INT32: case NEU_DTYPE_UINT32: data.type = MODBUS_B32; data.val.val_u32 = (uint32_t) i64; - ret = modbus_point_write(tag->addr_str, &data, - send_recv_callback, plugin); + error = modbus_point_write(tag->addr_str, &data, + send_recv_callback, plugin); break; case NEU_DTYPE_BIT: data.type = MODBUS_B8; data.val.val_u8 = (uint8_t) i64; - ret = modbus_point_write(tag->addr_str, &data, - send_recv_callback, plugin); + error = modbus_point_write(tag->addr_str, &data, + send_recv_callback, plugin); break; default: - ret = -1; + error = NEU_ERR_TAG_TYPE_NOT_SUPPORT; break; } break; @@ -379,27 +380,22 @@ static neu_data_val_t *setup_write_resp_data_value(neu_data_val_t *write_val, neu_dvalue_get_double(int_val->val, &db); data.type = MODBUS_B32; data.val.val_f32 = (float) db; - ret = modbus_point_write(tag->addr_str, &data, send_recv_callback, - plugin); + error = modbus_point_write(tag->addr_str, &data, send_recv_callback, + plugin); break; } default: - ret = -1; + error = NEU_ERR_EINTERNAL; break; } - if (ret == -1) { - resp_res = -1; - } - neu_dvalue_set_errorcode(val_error, ret); + + neu_dvalue_set_errorcode(val_error, error); neu_int_val_init(&iv, int_val->key, val_error); neu_fixed_array_set(array_resp, i, (void *) &iv); } - if (resp_res == -1) { - neu_dvalue_set_errorcode(val_error_resp, resp_res); - } else { - neu_dvalue_set_errorcode(val_error_resp, 0); - } + + neu_dvalue_set_errorcode(val_error_resp, error); neu_int_val_init(&int_val_resp, 0, val_error_resp); neu_fixed_array_set(array_resp, 0, (void *) &int_val_resp); @@ -498,9 +494,6 @@ static int modbus_tcp_close(neu_plugin_t *plugin) static int modbus_tcp_init(neu_plugin_t *plugin) { pthread_mutex_init(&plugin->mtx, NULL); - plugin->client = neu_tcp_client_create("192.168.50.68", 502); - - // plugin->common.state = NEURON_PLUGIN_STATE_READY; log_info("modbus tcp init....."); return 0; @@ -552,7 +545,12 @@ static int modbus_tcp_config(neu_plugin_t *plugin, neu_config_t *configs) &host); if (ret == 0) { - plugin->client = neu_tcp_client_create(host.v.val_str, port.v.val_int); + plugin->common.link_state = NEU_PLUGIN_LINK_STATE_CONNECTING; + plugin->client = neu_tcp_client_create(plugin->client, host.v.val_str, + port.v.val_int); + if (neu_tcp_client_is_connected(plugin->client)) { + plugin->common.link_state = NEU_PLUGIN_LINK_STATE_CONNECTED; + } } log_info("port = %d, connection_modem = %d, host = %s,ret = %d", @@ -598,7 +596,7 @@ static int modbus_tcp_request(neu_plugin_t *plugin, neu_request_t *req) neu_fixed_array_t *array = neu_fixed_array_new(1, sizeof(neu_int_val_t)); - neu_dvalue_set_errorcode(val_error, -1); + neu_dvalue_set_errorcode(val_error, NEU_ERR_GRP_NOT_SUBSCRIBE); neu_int_val_init(&val, 0, val_error); neu_fixed_array_set(array, 0, (void *) &val); neu_dvalue_init_move_array(array_resp, NEU_DTYPE_INT_VAL, array); diff --git a/plugins/restful/datatag_handle.c b/plugins/restful/datatag_handle.c index 3886effd..db796672 100644 --- a/plugins/restful/datatag_handle.c +++ b/plugins/restful/datatag_handle.c @@ -170,10 +170,9 @@ void handle_update_tags(nng_aio *aio) void handle_get_tags(nng_aio *aio) { - neu_plugin_t *plugin = neu_rest_get_plugin(); - char * result = NULL; - neu_node_id_t node_id = { 0 }; - char * s_grp_name = http_get_param(aio, "group_config_name"); + neu_plugin_t *plugin = neu_rest_get_plugin(); + char * result = NULL; + neu_node_id_t node_id = { 0 }; if (http_get_param_int(aio, "node_id", (int32_t *) &node_id) != 0) { NEU_JSON_RESPONSE_ERROR(NEU_ERR_PARAM_IS_WRONG, { @@ -182,6 +181,8 @@ void handle_get_tags(nng_aio *aio) return; } + char *s_grp_name = http_get_param(aio, "group_config_name"); + neu_datatag_table_t *table = neu_system_get_datatags_table(plugin, node_id); if (table == NULL) { NEU_JSON_RESPONSE_ERROR(NEU_ERR_NODE_NOT_EXIST, { diff --git a/plugins/restful/handle.h b/plugins/restful/handle.h index 9e7258ba..217d97bc 100644 --- a/plugins/restful/handle.h +++ b/plugins/restful/handle.h @@ -35,8 +35,9 @@ { func }; \ decode_fun##_free(req); \ } else { \ - http_bad_request(aio, "{\"error\": -1002}"); \ + http_bad_request(aio, "{\"error\": 1002}"); \ } \ + free(req_data); \ } enum neu_rest_method { diff --git a/plugins/restful/http.c b/plugins/restful/http.c index 2d73a37f..01035fec 100644 --- a/plugins/restful/http.c +++ b/plugins/restful/http.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -61,6 +62,10 @@ int http_get_body(nng_aio *aio, void **data, size_t *data_size) if (*data_size <= 0) { return -1; } else { + char *buf = calloc(*data_size + 1, sizeof(char)); + memcpy(buf, *data, *data_size); + log_info("body: %s", buf); + *data = buf; return 0; } } @@ -151,6 +156,7 @@ int http_response(nng_aio *aio, neu_err_code_e code, char *content) case NEU_ERR_GRP_CONFIG_NOT_EXIST: case NEU_ERR_TAG_NOT_EXIST: case NEU_ERR_NODE_SETTING_NOT_FOUND: + case NEU_ERR_GRP_NOT_SUBSCRIBE: status = NNG_HTTP_STATUS_NOT_FOUND; break; case NEU_ERR_NODE_EXIST: @@ -158,15 +164,18 @@ int http_response(nng_aio *aio, neu_err_code_e code, char *content) case NEU_ERR_NODE_IS_RUNNING: case NEU_ERR_NODE_NOT_RUNNING: case NEU_ERR_NODE_IS_STOPED: + case NEU_ERR_GRP_CONFIG_EXIST: status = NNG_HTTP_STATUS_CONFLICT; break; case NEU_ERR_TAG_ATTRIBUTE_NOT_SUPPORT: + case NEU_ERR_TAG_TYPE_NOT_SUPPORT: status = NNG_HTTP_STATUS_PARTIAL_CONTENT; break; case NEU_ERR_GRP_CONFIG_IN_USE: status = NNG_HTTP_STATUS_PRECONDITION_FAILED; break; default: + log_error("unhandle error code: %d", code); assert(code == 0); break; } diff --git a/plugins/restful/rw_handle.c b/plugins/restful/rw_handle.c index 22aaa62a..050dae29 100644 --- a/plugins/restful/rw_handle.c +++ b/plugins/restful/rw_handle.c @@ -167,14 +167,14 @@ void handle_write(nng_aio *aio) void handle_read_resp(void *cmd_resp) { - neu_request_t * req = (neu_request_t *) cmd_resp; - struct cmd_ctx * ctx = read_find_ctx(req->req_id); - neu_reqresp_read_resp_t *resp = (neu_reqresp_read_resp_t *) req->buf; - neu_fixed_array_t * array = NULL; - neu_json_read_resp_t api_res = { 0 }; - char * result = NULL; - neu_int_val_t * iv = NULL; - int32_t error_code = 0; + neu_request_t * req = (neu_request_t *) cmd_resp; + struct cmd_ctx * ctx = read_find_ctx(req->req_id); + neu_reqresp_read_resp_t *resp = (neu_reqresp_read_resp_t *) req->buf; + neu_fixed_array_t * array = NULL; + neu_json_read_resp_t api_res = { 0 }; + char * result = NULL; + neu_int_val_t * iv = NULL; + int32_t error = 0; log_info("read resp id: %d, ctx: %p", req->req_id, ctx); assert(ctx != NULL); @@ -185,9 +185,10 @@ void handle_read_resp(void *cmd_resp) assert(iv->key == 0); assert(neu_dvalue_get_value_type(iv->val) == NEU_DTYPE_ERRORCODE); - neu_dvalue_get_errorcode(iv->val, &error_code); - if (error_code != 0) { - http_bad_request(ctx->aio, "{\"error\": 1}"); + neu_dvalue_get_errorcode(iv->val, &error); + if (error != 0) { + NEU_JSON_RESPONSE_ERROR( + error, { http_response(ctx->aio, error_code.error, result_error); }) free(ctx); return; } @@ -263,12 +264,24 @@ void handle_read_resp(void *cmd_resp) void handle_write_resp(void *cmd_resp) { - neu_request_t * req = (neu_request_t *) cmd_resp; - struct cmd_ctx *ctx = write_find_ctx(req->req_id); + neu_request_t * req = (neu_request_t *) cmd_resp; + struct cmd_ctx * ctx = write_find_ctx(req->req_id); + neu_reqresp_write_resp_t *resp = (neu_reqresp_write_resp_t *) req->buf; + neu_fixed_array_t * array = NULL; + neu_int_val_t * iv = NULL; + int32_t error = 0; log_info("write resp id: %d, ctx: %p", req->req_id, ctx); + + neu_dvalue_get_ref_array(resp->data_val, &array); + iv = (neu_int_val_t *) neu_fixed_array_get(array, 0); + assert(iv->key == 0); + assert(neu_dvalue_get_value_type(iv->val) == NEU_DTYPE_ERRORCODE); + neu_dvalue_get_errorcode(iv->val, &error); + if (ctx != NULL) { - http_ok(ctx->aio, "{\"status\": \"OK\"}"); + NEU_JSON_RESPONSE_ERROR( + error, { http_response(ctx->aio, error_code.error, result_error); }) free(ctx); } assert(ctx != NULL); diff --git a/simulator/CMakeLists.txt b/simulator/CMakeLists.txt new file mode 100644 index 00000000..df64a82e --- /dev/null +++ b/simulator/CMakeLists.txt @@ -0,0 +1,3 @@ +add_executable(modbus_tcp_server_simulator modbus_tcp_server_sim.c) +target_include_directories(modbus_tcp_server_simulator PRIVATE ${CMAKE_SOURCE_DIR}) +target_link_libraries(modbus_tcp_server_simulator neuron-base) \ No newline at end of file diff --git a/simulator/modbus_tcp_server_sim.c b/simulator/modbus_tcp_server_sim.c new file mode 100644 index 00000000..6e67d66a --- /dev/null +++ b/simulator/modbus_tcp_server_sim.c @@ -0,0 +1,247 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "include/connection/neu_tcp.h" +#include "include/neu_log.h" +#include "plugins/modbus/modbus.h" + +static bool run = true; + +static void sig_handle(int sig) +{ + log_warn("recv sig: %d", sig); + run = false; +} + +static uint8_t coil[1024] = { 0 }; +static uint8_t input[1024] = { 0 }; +static uint16_t input_reg[1024] = { 0 }; +static uint16_t hold_reg[1024] = { 0 }; + +void input_init() +{ + for (uint32_t i = 0; i < 1024; i++) { + if (i % 2 == 0) { + input[i] = 0; + } else { + input[i] = 1; + } + } + + for (uint32_t i = 0; i < 1024; i++) { + input_reg[i] = (uint16_t) i; + } +} + +int main() +{ + neu_tcp_server_t *server = neu_tcp_server_create("127.0.0.1", 60502); + bool new_client = true; + int ret = 0; + + if (server == NULL) { + return -1; + } + + input_init(); + + signal(SIGINT, sig_handle); + signal(SIGTERM, sig_handle); + signal(SIGABRT, sig_handle); + + while (run) { + char buf[64] = { 0 }; + char body_buf[1024] = { 0 }; + char response[4096] = { 0 }; + ssize_t res_len = 0; + ssize_t len = 0; + uint8_t *w_u8 = NULL; + + struct modbus_header * header = (struct modbus_header *) buf; + struct modbus_code * code = (struct modbus_code *) body_buf; + struct modbus_pdu_read_request *r_req = NULL; + struct modbus_pdu_m_write_request *w_req = NULL; + + struct modbus_header *res_header = (struct modbus_header *) response; + struct modbus_code * res_code = (struct modbus_code *) &res_header[1]; + struct modbus_pdu_read_response * r_res = NULL; + struct modbus_pdu_write_response *w_res = NULL; + + res_len = sizeof(struct modbus_header) + sizeof(struct modbus_code); + + if (new_client) { + ret = neu_tcp_server_wait_client(server); + if (ret != 0) { + sleep(3); + continue; + } else { + new_client = false; + } + } + + len = neu_tcp_server_recv(server, (char *) header, + sizeof(struct modbus_header), + sizeof(struct modbus_header)); + if (len <= 0) { + log_warn("recv header error, %d", len); + new_client = true; + sleep(1); + continue; + } + + if (ntohs(header->len) <= 2) { + log_warn("head len error, %d", ntohs(header->len)); + new_client = true; + sleep(1); + continue; + } + + len = neu_tcp_server_recv(server, body_buf, sizeof(body_buf), + ntohs(header->len)); + if (len <= 0) { + log_warn("recv code error, %d", len); + new_client = true; + sleep(1); + continue; + } + + switch (code->function_code) { + case MODBUS_READ_COIL: + case MODBUS_READ_INPUT_CONTACT: + case MODBUS_READ_HOLD_REG: + case MODBUS_READ_INPUT_REG: + r_req = (struct modbus_pdu_read_request *) &code[1]; + r_res = (struct modbus_pdu_read_response *) &res_code[1]; + res_len += sizeof(struct modbus_pdu_read_response); + break; + case MODBUS_WRITE_S_COIL: + case MODBUS_WRITE_S_HOLD_REG: + case MODBUS_WRITE_M_HOLD_REG: + case MODBUS_WRITE_M_COIL: + w_req = (struct modbus_pdu_m_write_request *) &code[1]; + w_res = (struct modbus_pdu_write_response *) &res_code[1]; + w_u8 = (uint8_t *) &w_req[1]; + res_len += sizeof(struct modbus_pdu_write_response); + break; + } + + switch (code->function_code) { + case MODBUS_READ_COIL: + case MODBUS_READ_INPUT_CONTACT: { + r_res->n_byte = ntohs(r_req->n_reg) / 8; + if (ntohs(r_req->n_reg) % 8 != 0) { + r_res->n_byte += 1; + } + + uint8_t *byte = (uint8_t *) &r_res[1]; + + for (int i = 0; i < ntohs(r_req->n_reg); i++) { + if (code->function_code == MODBUS_READ_COIL) { + byte[i / 8] = byte[i / 8] | + coil[ntohs(r_req->start_addr) + i] << (i % 8); + } else { + byte[i / 8] = byte[i / 8] | + input[ntohs(r_req->start_addr) + i] << (i % 8); + log_info("read, addr: %d, i: %d, value: %d, n_reg: %d, " + "byte: %d, " + "dvluae: %d", + ntohs(r_req->start_addr) + i, i, + input[ntohs(r_req->start_addr) + i], + ntohs(r_req->n_reg), byte[i / 8], + input[ntohs(r_req->start_addr) + i] << (i % 8)); + } + } + + res_len += r_res->n_byte; + + break; + } + case MODBUS_READ_HOLD_REG: + case MODBUS_READ_INPUT_REG: + r_res->n_byte = ntohs(r_req->n_reg) * 2; + + uint16_t *byte = (uint16_t *) &r_res[1]; + + for (int i = 0; i < ntohs(r_req->n_reg); i++) { + if (code->function_code == MODBUS_READ_HOLD_REG) { + byte[i] = htons(hold_reg[ntohs(r_req->start_addr) + i]); + } else { + byte[i] = htons(input_reg[ntohs(r_req->start_addr) + i]); + } + // log_info("read addr: %d, value: %d, i:%d", + // ntohs(r_req->start_addr) + i, byte[i], i); + } + + res_len += r_res->n_byte; + // log_info("read resp: n_byte: %d,len :%d", + // r_res->n_byte, res_len); + + break; + case MODBUS_WRITE_S_COIL: + case MODBUS_WRITE_S_HOLD_REG: + break; + case MODBUS_WRITE_M_HOLD_REG: + w_res->start_addr = w_req->start_addr; + w_res->n_reg = w_req->n_reg; + + for (int i = 0; i < w_req->n_byte; i++) { + if (i % 2 == 0) { + hold_reg[ntohs(w_req->start_addr) + i / 2] = + hold_reg[ntohs(w_req->start_addr) + i / 2] | + (w_u8[i] << 8); + } else { + hold_reg[ntohs(w_req->start_addr) + i / 2] = + hold_reg[ntohs(w_req->start_addr) + i / 2] | w_u8[i]; + } + log_info("write coil addr: %d, value: %X, reg: %d", + ntohs(w_req->start_addr) + i / 2, w_u8[i], + hold_reg[ntohs(w_req->start_addr) + i / 2]); + } + + res_len += sizeof(struct modbus_pdu_write_response); + break; + case MODBUS_WRITE_M_COIL: { + w_res->start_addr = w_req->start_addr; + w_res->n_reg = w_req->n_reg; + + for (int i = 0; i < ntohs(w_req->n_reg); i++) { + uint8_t x = w_u8[ntohs(w_req->n_reg) / 8] << (7 - (i % 8)); + + if (x > 0) { + coil[ntohs(w_req->start_addr) + i] = 1; + } else { + coil[ntohs(w_req->start_addr) + i] = 0; + } + log_info("write coil addr: %d, value: %d", + ntohs(w_req->start_addr) + i, x > 0 ? 1 : 0); + } + + res_len += sizeof(struct modbus_pdu_write_response); + break; + } + } + + res_header->process_no = header->process_no; + res_header->flag = header->flag; + res_header->len = htons(res_len - sizeof(struct modbus_header)); + res_code->device_address = code->device_address; + res_code->function_code = code->function_code; + + len = neu_tcp_server_send(server, response, res_len); + if (len <= 0) { + log_warn("send response %d", res_len); + new_client = true; + sleep(1); + continue; + } + } + + neu_tcp_server_close(server); + + return 0; +} \ No newline at end of file diff --git a/src/connection/tcp_client.c b/src/connection/tcp_client.c index 2e91a548..60c5d888 100644 --- a/src/connection/tcp_client.c +++ b/src/connection/tcp_client.c @@ -19,7 +19,9 @@ #include #include +#include #include +#include #include #include "neu_log.h" @@ -27,28 +29,63 @@ #include "connection/neu_tcp.h" struct neu_tcp_client { - in_addr_t server; - uint16_t port; - int fd; + in_addr_t server; + uint16_t port; + int fd; + pthread_mutex_t mtx; }; static int client_connect(neu_tcp_client_t *client); -neu_tcp_client_t *neu_tcp_client_create(char *server, uint16_t port) +neu_tcp_client_t *neu_tcp_client_create(neu_tcp_client_t *client, char *server, + uint16_t port) { - neu_tcp_client_t *client = - (neu_tcp_client_t *) calloc(1, sizeof(neu_tcp_client_t)); + if (client == NULL) { + client = (neu_tcp_client_t *) calloc(1, sizeof(neu_tcp_client_t)); + pthread_mutex_init(&client->mtx, NULL); + } else { + pthread_mutex_lock(&client->mtx); + close(client->fd); + client->fd = -1; + pthread_mutex_unlock(&client->mtx); + } + pthread_mutex_lock(&client->mtx); client->port = htons(port); client->server = inet_addr(server); client->fd = client_connect(client); + pthread_mutex_unlock(&client->mtx); return client; } +bool neu_tcp_client_is_connected(neu_tcp_client_t *client) +{ + bool ret = false; + + if (client == NULL) { + return ret; + } + + pthread_mutex_lock(&client->mtx); + ret = client->fd > 0; + pthread_mutex_unlock(&client->mtx); + + return ret; +} + void neu_tcp_client_close(neu_tcp_client_t *client) { + if (client == NULL) { + return; + } + pthread_mutex_lock(&client->mtx); close(client->fd); + client->fd = -1; + pthread_mutex_unlock(&client->mtx); + + pthread_mutex_destroy(&client->mtx); + free(client); } @@ -56,26 +93,39 @@ ssize_t neu_tcp_client_send_recv(neu_tcp_client_t *client, char *send_buf, ssize_t send_len, char *recv_buf, ssize_t recv_len) { + if (client == NULL) { + return -1; + } + + pthread_mutex_lock(&client->mtx); if (client->fd < 0) { client->fd = client_connect(client); + pthread_mutex_unlock(&client->mtx); return -1; - } else { - ssize_t ret = send(client->fd, send_buf, send_len, 0); - if (ret != send_len) { - log_error("tcp client send error, ret: %d, len: %d, errno: %d", ret, - send_len, errno); - return -1; - } - - ret = recv(client->fd, recv_buf, recv_len, 0); - if (ret == -1) { - log_error("tcp client recv error, ret: %d, len: %d, errno: %d", ret, - recv_len, errno); - return -1; - } - - return ret; } + + ssize_t ret = send(client->fd, send_buf, send_len, 0); + if (ret != send_len) { + log_error("tcp client send error, ret: %d, len: %d, errno: %d", ret, + send_len, errno); + close(client->fd); + client->fd = -1; + pthread_mutex_unlock(&client->mtx); + return -1; + } + + ret = recv(client->fd, recv_buf, recv_len, 0); + if (ret == -1) { + log_error("tcp client recv error, ret: %d, len: %d, errno: %d", ret, + recv_len, errno); + close(client->fd); + client->fd = -1; + pthread_mutex_unlock(&client->mtx); + return -1; + } + + pthread_mutex_unlock(&client->mtx); + return ret; } static int client_connect(neu_tcp_client_t *client) @@ -84,7 +134,7 @@ static int client_connect(neu_tcp_client_t *client) int fd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addr = { 0 }; struct timeval tm = { - .tv_sec = 1, + .tv_sec = 5, .tv_usec = 0, }; @@ -98,8 +148,9 @@ static int client_connect(neu_tcp_client_t *client) ret = connect(fd, (struct sockaddr *) &addr, sizeof(struct sockaddr_in)); if (ret != 0) { - log_error("connect %d:%d error: %d", client->server, - ntohs(client->server), errno); + struct in_addr tmp = { .s_addr = client->server }; + log_error("connect %s:%d error: %s", inet_ntoa(tmp), + ntohs(client->port), strerror(errno)); return -1; } diff --git a/src/connection/tcp_server.c b/src/connection/tcp_server.c index f377ea1c..5b0f1008 100644 --- a/src/connection/tcp_server.c +++ b/src/connection/tcp_server.c @@ -17,4 +17,182 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ +#include +#include +#include +#include +#include +#include + +#include "neu_log.h" + #include "connection/neu_tcp.h" + +struct neu_tcp_server { + in_addr_t host; + uint16_t port; + int listen_fd; + int client_fd; +}; + +neu_tcp_server_t *neu_tcp_server_create(char *local_host, uint16_t local_port) +{ + neu_tcp_server_t * server = calloc(1, sizeof(neu_tcp_server_t)); + struct sockaddr_in local = { 0 }; + int ret = 0; + + server->host = inet_addr(local_host); + server->port = htons(local_port); + server->listen_fd = + socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP); + + // signal(SIGPIPE, SIG_IGN); + + if (server->listen_fd < 0) { + log_error("new socket fail, errno: %s", strerror(errno)); + free(server); + return NULL; + } + + local.sin_family = AF_INET; + local.sin_port = server->port; + local.sin_addr.s_addr = server->host; + + ret = bind(server->listen_fd, (struct sockaddr *) &local, sizeof(local)); + + if (ret != 0) { + log_error("bind: %s:%d fail, errno: %s", local_host, local_port, + strerror(errno)); + close(server->listen_fd); + free(server); + return NULL; + } + + ret = listen(server->listen_fd, 1); + + if (ret != 0) { + log_error("listen: %s:%d fail, errno: %s", local_host, local_port, + strerror(errno)); + close(server->listen_fd); + free(server); + return NULL; + } + + log_info("listen: %s:%d success, listen fd: %d", local_host, local_port, + server->listen_fd); + return server; +} + +int neu_tcp_server_wait_client(neu_tcp_server_t *server) +{ + struct sockaddr_in client = { 0 }; + socklen_t client_len = 0; + int fd = 0; + struct timeval tv = { .tv_sec = 5, .tv_usec = 0 }; + + if (server->client_fd != 0) { + close(server->client_fd); + server->client_fd = 0; + } + + fd = accept(server->listen_fd, (struct sockaddr *) &client, &client_len); + if (fd <= 0) { + log_error("accept error: %s", strerror(errno)); + return -1; + } + + setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); + + server->client_fd = fd; + + log_info("accept new client: %s:%d, client fd: %d, listen fd: %d", + inet_ntoa(client.sin_addr), ntohs(client.sin_port), fd, + server->listen_fd); + + return 0; +} + +void neu_tcp_server_close(neu_tcp_server_t *server) +{ + close(server->client_fd); + close(server->listen_fd); + + free(server); +} + +ssize_t neu_tcp_server_send_recv(neu_tcp_server_t *server, char *send_buf, + ssize_t send_len, char *recv_buf, + ssize_t recv_len) +{ + ssize_t ret = 0; + + if (server->client_fd <= 0) { + log_error("no client connected to the server, listen fd: %d", + server->listen_fd); + return -1; + } + + ret = send(server->client_fd, send_buf, send_len, MSG_NOSIGNAL); + if (ret <= 0 || ret != send_len) { + log_error("send buf error, ret: %d, errno: %s, len: %d", ret, + strerror(errno), send_len); + return -1; + } + + ret = recv(server->client_fd, recv_buf, recv_len, MSG_WAITALL); + if (ret <= 0 || ret != recv_len) { + log_error("recv buf error, ret: %d, errno: %s, len: %d", ret, + strerror(errno), recv_len); + return -1; + } + + return 0; +} + +ssize_t neu_tcp_server_recv(neu_tcp_server_t *server, char *recv_buf, + ssize_t buf_len, ssize_t recv_len) +{ + ssize_t ret = 0; + + if (server->client_fd <= 0) { + log_error("no client connected to the server, listen fd: %d", + server->listen_fd); + return -1; + } + + if (recv_len > 0) { + ret = recv(server->client_fd, recv_buf, recv_len, MSG_WAITALL); + } else { + ret = recv(server->client_fd, recv_buf, buf_len, 0); + } + + if (ret <= 0) { + log_error("recv buf error, ret: %d, errno: %s, len: %d", ret, + strerror(errno), recv_len); + return -1; + } + + return ret; +} + +ssize_t neu_tcp_server_send(neu_tcp_server_t *server, char *send_buf, + ssize_t send_len) +{ + ssize_t ret = 0; + + if (server->client_fd <= 0) { + log_error("no client connected to the server, listen fd: %d", + server->listen_fd); + return -1; + } + + ret = send(server->client_fd, send_buf, send_len, MSG_NOSIGNAL); + if (ret <= 0 || ret != send_len) { + log_error("send buf error, ret: %d, errno: %s, len: %d", ret, + strerror(errno), send_len); + return -1; + } + + return ret; +} \ No newline at end of file diff --git a/src/core/neu_datatag_manager.c b/src/core/neu_datatag_manager.c index 719ad048..d25213be 100644 --- a/src/core/neu_datatag_manager.c +++ b/src/core/neu_datatag_manager.c @@ -137,7 +137,7 @@ void neu_datatag_mng_destroy(neu_datatag_manager_t *datatag_manager) int neu_datatag_mng_add_grp_config(neu_datatag_manager_t *datatag_manager, neu_taggrp_config_t * grp_config) { - int rv = -1; + int rv = NEU_ERR_GRP_CONFIG_EXIST; size_t index; pthread_mutex_lock(&datatag_manager->mtx); @@ -149,7 +149,7 @@ int neu_datatag_mng_add_grp_config(neu_datatag_manager_t *datatag_manager, } pthread_mutex_unlock(&datatag_manager->mtx); - if (rv == 0) { + if (rv == NEU_ERR_SUCCESS) { log_info("Add group config: %s", neu_taggrp_cfg_get_name(grp_config)); } return rv; diff --git a/src/core/neu_manager.c b/src/core/neu_manager.c index 3271eb8f..19a223bc 100644 --- a/src/core/neu_manager.c +++ b/src/core/neu_manager.c @@ -854,7 +854,7 @@ static int unsub_grp_config_with_adapter(neu_manager_t * manager, static int add_grp_config_to_adapter(adapter_reg_entity_t *reg_entity, neu_taggrp_config_t * grp_config) { - int rv = 0; + int rv = NEU_ERR_SUCCESS; neu_datatag_manager_t *datatag_mng; datatag_mng = reg_entity->datatag_manager; @@ -1675,18 +1675,18 @@ int neu_manager_get_nodes(neu_manager_t *manager, neu_node_type_e node_type, int neu_manager_add_grp_config(neu_manager_t * manager, neu_cmd_add_grp_config_t *cmd) { - int rv = 0; + int rv = NEU_ERR_SUCCESS; adapter_id_t adapter_id; adapter_reg_entity_t *reg_entity; if (manager == NULL || cmd == NULL) { log_error("Add group config with NULL manager or command"); - return -1; + return NEU_ERR_EINTERNAL; } if (cmd->grp_config == NULL) { log_error("group config is NULL"); - return -1; + return NEU_ERR_EINTERNAL; } nng_mtx_lock(manager->adapters_mtx); diff --git a/src/utils/json.c b/src/utils/json.c index 859e6a13..1d9541fc 100644 --- a/src/utils/json.c +++ b/src/utils/json.c @@ -151,9 +151,9 @@ int neu_json_decode(char *buf, int size, neu_json_elem_t *ele) json_t * root = json_loads(buf, 0, &error); if (root == NULL) { - log_error( - "json load error, line: %d, column: %d, position: %d, info: %s", - error.line, error.column, error.position, error.text); + log_error("json load error, line: %d, column: %d, position: %d, info: " + "%s", + error.line, error.column, error.position, error.text); return -1; }