forked from espressif/esp-modbus
Merge branch 'feature/modbus_tcp_master_slave_v2' into 'release/v2.0'
feature modbus tcp master slave v2 See merge request idf/esp-modbus!69
This commit is contained in:
118
.gitlab-ci.yml
118
.gitlab-ci.yml
@ -15,6 +15,18 @@ variables:
|
||||
ARTIFACT_DOWNLOAD_ATTEMPTS: "10"
|
||||
GIT_SUBMODULE_STRATEGY: none
|
||||
|
||||
# Define a matrix for IDF versions and their corresponding targets
|
||||
.options_list:
|
||||
# versions:
|
||||
# IDF_VER: ["latest", "v5.3", "v5.2", "v5.0"]
|
||||
markers:
|
||||
TEST_MARKER:
|
||||
- "tcp"
|
||||
- "serial"
|
||||
- "generic"
|
||||
# - "tcp_p4"
|
||||
# - "serial_p4"
|
||||
|
||||
.setup_idf_tools: &setup_idf_tools |
|
||||
tools/idf_tools.py --non-interactive install && eval "$(tools/idf_tools.py --non-interactive export)" || exit 1
|
||||
|
||||
@ -61,7 +73,10 @@ after_script:
|
||||
echo "${IDF_VERSION}"
|
||||
fi
|
||||
|
||||
# Note: this script builds the folder against all targets and then deletes
|
||||
# all other artifacts except the esp32 to decrease the size of artifacs (may cause failures)
|
||||
.build_cur_folder: &build_cur_folder |
|
||||
echo "Build job ${CI_JOB_NAME}, folder: ${PWD##*/}, targets: ${TEST_TARGETS}"
|
||||
python -m idf_build_apps build -v -p . \
|
||||
--recursive \
|
||||
--target all \
|
||||
@ -88,7 +103,20 @@ after_script:
|
||||
extends:
|
||||
- .build_template
|
||||
- .before_script_build_jobs
|
||||
script:
|
||||
# The script below will build all test applications defined in environment variable $TEST_TARGETS
|
||||
- *check_idf_ver
|
||||
# This is workaround to build library under esp-idf v4.4
|
||||
- pip install idf-component-manager --upgrade
|
||||
- cd ${TEST_DIR}/test_apps
|
||||
- *build_cur_folder
|
||||
- cd ${TEST_DIR}/examples
|
||||
- export TEST_TARGETS="esp32" # override to build only on esp32 target to decrease build time
|
||||
- *build_cur_folder
|
||||
variables:
|
||||
TEST_TARGETS: "esp32"
|
||||
artifacts:
|
||||
name: artifacts_${CI_JOB_NAME}
|
||||
paths:
|
||||
- "**/build*/size.json"
|
||||
- "**/build*/build.log"
|
||||
@ -106,30 +134,24 @@ after_script:
|
||||
- $SIZE_INFO_LOCATION
|
||||
when: always
|
||||
expire_in: 3 weeks
|
||||
script:
|
||||
# The script below will build all test applications defined in environment variable $TEST_TARGETS
|
||||
- *check_idf_ver
|
||||
# This is workaround to build library under esp-idf v4.4
|
||||
- pip install idf-component-manager --upgrade
|
||||
- cd ${TEST_DIR}/test_apps
|
||||
- *build_cur_folder
|
||||
- cd ${TEST_DIR}/examples
|
||||
- export TEST_TARGETS="esp32" # override to build only on esp32 target to decrease build time
|
||||
- *build_cur_folder
|
||||
variables:
|
||||
TEST_TARGETS: "esp32"
|
||||
|
||||
build_idf_master:
|
||||
extends: .build_pytest_template
|
||||
image: espressif/idf:latest
|
||||
variables:
|
||||
TEST_TARGETS: "esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 esp32h2"
|
||||
build_idf_latest:
|
||||
extends: .build_pytest_template
|
||||
image: espressif/idf:latest
|
||||
variables:
|
||||
TEST_TARGETS: "esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c5 esp32c6 esp32h2"
|
||||
|
||||
build_idf_v5.3:
|
||||
extends: .build_pytest_template
|
||||
image: espressif/idf:release-v5.3
|
||||
variables:
|
||||
TEST_TARGETS: "esp32 esp32s2 esp32s3 esp32c2 esp32c6 esp32c3"
|
||||
TEST_TARGETS: "esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 esp32h2"
|
||||
|
||||
build_idf_v5.2:
|
||||
extends: .build_pytest_template
|
||||
image: espressif/idf:release-v5.2
|
||||
variables:
|
||||
TEST_TARGETS: "esp32 esp32s2 esp32s3 esp32c2 esp32c3"
|
||||
|
||||
build_idf_v5.0:
|
||||
extends: .build_pytest_template
|
||||
@ -156,66 +178,83 @@ build_idf_v5.0:
|
||||
# Install or upgrade pytest-embedded to perform test cases
|
||||
- pip install --only-binary cryptography pytest-embedded pytest-embedded-serial-esp pytest-embedded-idf --upgrade
|
||||
|
||||
.test_cur_folder: &test_cur_folder |
|
||||
export IDF_VER=$(cat ${TEST_DIR}/idf_version_info.txt)
|
||||
echo "Start test job: ${CI_JOB_NAME}, version: ${IDF_VER%-*}, folder: ${PWD##*/}"
|
||||
python -m pytest --junit-xml=${TEST_DIR}/${PWD##*/}/results_${IDF_VER%-*}_${PWD##*/}.xml --target=${IDF_TARGET} -m multi_dut_modbus_${TEST_MARKER}
|
||||
ls -lh > test_dir_${PWD##*/}.txt
|
||||
|
||||
.test_template:
|
||||
image: "$CI_DOCKER_REGISTRY/target-test-env-v5.3:1"
|
||||
stage: target_test
|
||||
extends:
|
||||
- .before_script_pytest_jobs
|
||||
tags:
|
||||
- multi_dut_modbus_${TEST_PORT}
|
||||
- multi_dut_modbus_${TEST_MARKER}
|
||||
variables:
|
||||
IDF_TARGET: "esp32" # the only esp32 runners are available for now
|
||||
script:
|
||||
- cd ${TEST_DIR}/test_apps/
|
||||
- *test_cur_folder
|
||||
- cd ${TEST_DIR}/examples/
|
||||
- *test_cur_folder
|
||||
artifacts:
|
||||
name: artifacts_${CI_JOB_NAME}
|
||||
paths:
|
||||
- "${TEST_DIR}/**/*.log"
|
||||
- "${TEST_DIR}/**/*.xml"
|
||||
- "${TEST_DIR}/**/results_*.xml"
|
||||
- "${TEST_DIR}/**/pytest_embedded_log/"
|
||||
- "${TEST_DIR}/**/test_dir*.txt"
|
||||
- "${TEST_DIR}/**/test_dir*.txt"
|
||||
- "${TEST_DIR}/**/idf_version_info.txt"
|
||||
reports:
|
||||
junit: ${TEST_DIR}/${TEST_SUBDIR}/results_${IDF_VER%-*}_${TEST_SUBDIR%%/*}_${TEST_PORT}.xml
|
||||
junit: ${TEST_DIR}/results_${IDF_VER%-*}.xml
|
||||
when: always
|
||||
expire_in: 1 week
|
||||
script:
|
||||
- cd ${TEST_DIR}/${TEST_SUBDIR}/
|
||||
- export IDF_VER=$(cat ${TEST_DIR}/idf_version_info.txt)
|
||||
- echo "Start test for [${IDF_VER%-*}_${TEST_SUBDIR%%/*}-${TEST_SUBDIR##*/}_${TEST_PORT}]"
|
||||
- python -m pytest --junit-xml=${TEST_DIR}/${TEST_SUBDIR}/results_${IDF_VER%-*}_${TEST_SUBDIR%%/*}_${TEST_PORT}.xml --target=${IDF_TARGET}
|
||||
- ls -lh > test_dir_${PWD##*/}.txt
|
||||
|
||||
target_test_master:
|
||||
target_test_latest:
|
||||
stage: target_test
|
||||
image: "$CI_DOCKER_REGISTRY/target-test-env-v5.3:1"
|
||||
image: "$CI_DOCKER_REGISTRY/target-test-env-v5.4:1"
|
||||
extends: .test_template
|
||||
parallel:
|
||||
matrix:
|
||||
- TEST_PORT: ["serial"]
|
||||
TEST_SUBDIR: ["examples/serial", "test_apps"] # test only serial examples for now
|
||||
- !reference [.options_list, markers]
|
||||
needs:
|
||||
job: build_idf_master
|
||||
job: build_idf_latest
|
||||
artifacts: true
|
||||
after_script: []
|
||||
|
||||
target_test_5.3:
|
||||
target_test_v5.3:
|
||||
stage: target_test
|
||||
image: "$CI_DOCKER_REGISTRY/target-test-env-v5.3:1"
|
||||
extends: .test_template
|
||||
parallel:
|
||||
matrix:
|
||||
- TEST_PORT: ["serial"]
|
||||
TEST_SUBDIR: ["examples/serial", "test_apps"] # test only serial examples for now
|
||||
- !reference [.options_list, markers]
|
||||
needs:
|
||||
job: build_idf_v5.3
|
||||
artifacts: true
|
||||
after_script: []
|
||||
|
||||
target_test_v5.2:
|
||||
stage: target_test
|
||||
image: "$CI_DOCKER_REGISTRY/target-test-env-v5.2:2"
|
||||
extends: .test_template
|
||||
parallel:
|
||||
matrix:
|
||||
- !reference [.options_list, markers]
|
||||
needs:
|
||||
job: build_idf_v5.2
|
||||
artifacts: true
|
||||
after_script: []
|
||||
|
||||
target_test_v5.0:
|
||||
stage: target_test
|
||||
image: "$CI_DOCKER_REGISTRY/target-test-env-v5.0:3"
|
||||
extends: .test_template
|
||||
parallel:
|
||||
matrix:
|
||||
- TEST_PORT: ["serial"]
|
||||
TEST_SUBDIR: ["examples/serial", "test_apps"]
|
||||
- !reference [.options_list, markers]
|
||||
needs:
|
||||
job: build_idf_v5.0
|
||||
artifacts: true
|
||||
@ -293,9 +332,10 @@ upload_to_component_manager:
|
||||
tags:
|
||||
- deploy
|
||||
rules:
|
||||
- if: '$CI_COMMIT_BRANCH == "master"'
|
||||
- if: '$CI_COMMIT_BRANCH == "release/v2.0"'
|
||||
- if: '$FORCE_PUSH_COMPONENT == "1"'
|
||||
script:
|
||||
- pip install idf-component-manager
|
||||
- export IDF_COMPONENT_API_TOKEN=${ESP_MODBUS_API_KEY}
|
||||
- python -m idf_component_manager upload-component --skip-pre-release --name=esp-modbus --namespace=espressif --version=2.0.0
|
||||
- export COMP_VERSION=$(grep 'version:' idf_component.yml | head -n 1 | awk '{print $2}' | tr -d '"')
|
||||
- compote component upload --namespace=espressif --name=esp-modbus --allow-existing --version=${COMP_VERSION}
|
||||
|
@ -27,6 +27,7 @@ set(srcs
|
||||
"mb_ports/common/port_event.c"
|
||||
"mb_ports/common/port_other.c"
|
||||
"mb_ports/common/port_timer.c"
|
||||
"mb_ports/common/mb_transaction.c"
|
||||
"mb_ports/serial/port_serial.c"
|
||||
"mb_ports/tcp/port_tcp_master.c"
|
||||
"mb_ports/tcp/port_tcp_slave.c"
|
||||
@ -79,11 +80,13 @@ foreach(req ${optional_reqs} ${exclude_comps})
|
||||
if(req IN_LIST build_components)
|
||||
idf_component_get_property(req_lib ${req} COMPONENT_LIB)
|
||||
target_link_libraries(${COMPONENT_LIB} PRIVATE ${req_lib})
|
||||
message(STATUS "Req ${req} is found and added into ${COMPONENT_NAME} dependencies.") # FATAL_ERROR
|
||||
target_compile_definitions(${COMPONENT_LIB} PUBLIC -DMB_MDNS_IS_INCLUDED)
|
||||
message(STATUS "Req ${req} is found and added into ${COMPONENT_NAME} dependencies.")
|
||||
target_compile_definitions(${COMPONENT_LIB} PUBLIC -DMB_MDNS_IS_INCLUDED)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# target_link_options(${COMPONENT_LIB} INTERFACE -fsanitize=undefined -fsanitize=alignment) #-fsanitize=address -fsanitize=undefined
|
||||
# target_link_options(${COMPONENT_LIB} INTERFACE -fsanitize=address)
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-strict-aliasing -Wno-write-strings -Werror)
|
||||
|
||||
message(STATUS "The mdns included is: ${MB_MDNS_IS_INCLUDED}")
|
||||
|
@ -1 +1 @@
|
||||
esp-docs>=1.8,<2.0
|
||||
esp-docs>=1.9.1,<2.0
|
@ -1,18 +1,27 @@
|
||||
tcp/mb_tcp_master:
|
||||
disable_test:
|
||||
- if: IDF_TARGET != "esp32"
|
||||
- if: IDF_TARGET != "esp32" or CONFIG_NAME == "dummy_config"
|
||||
reason: only manual test is performed
|
||||
disable:
|
||||
# TCP port is not supported for now
|
||||
- if: CONFIG_NAME == "wifi" or CONFIG_NAME == "ethernet"
|
||||
# - if: CONFIG_NAME == "wifi" and SOC_WIFI_SUPPORTED != 1
|
||||
- if: CONFIG_NAME == "wifi" and SOC_WIFI_SUPPORTED != 1
|
||||
|
||||
tcp/mb_tcp_slave:
|
||||
disable_test:
|
||||
- if: IDF_TARGET != "esp32"
|
||||
- if: IDF_TARGET != "esp32" or CONFIG_NAME == "dummy_config"
|
||||
reason: only manual test is performed
|
||||
disable:
|
||||
- if: CONFIG_NAME == "wifi" or CONFIG_NAME == "ethernet"
|
||||
# - if: CONFIG_NAME == "wifi" and SOC_WIFI_SUPPORTED != 1
|
||||
- if: CONFIG_NAME == "wifi" and SOC_WIFI_SUPPORTED != 1
|
||||
|
||||
serial/mb_serial_master:
|
||||
disable_test:
|
||||
- if: IDF_TARGET != "esp32" or CONFIG_NAME == "dummy_config"
|
||||
reason: only manual test is performed
|
||||
disable:
|
||||
- if: CONFIG_NAME == "default" and SOC_WIFI_SUPPORTED != 1
|
||||
|
||||
serial/mb_serial_slave:
|
||||
disable_test:
|
||||
- if: IDF_TARGET != "esp32" or CONFIG_NAME == "dummy_config"
|
||||
reason: only manual test is performed
|
||||
|
||||
|
||||
|
@ -39,7 +39,7 @@ class ModbusTestDut(IdfDut):
|
||||
TEST_IP_PROMPT = r'Waiting IP([0-9]{1,2}) from stdin:\r\r\n'
|
||||
TEST_IP_SET_CONFIRM = r'.*IP\([0-9]+\) = \[([0-9a-zA-Z\.\:]+)\] set from stdin.*'
|
||||
TEST_IP_ADDRESS_REGEXP = r'.*example_[a-z]+: .* IPv4 [a-z]+:.* ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}).*'
|
||||
TEST_APP_NAME = r'.*Project name: ([_a-z]*)'
|
||||
TEST_APP_NAME = r'I \([0-9]+\) [a-z_]+: Project name:\s+([_a-z]*)'
|
||||
|
||||
TEST_EXPECT_STR_TIMEOUT = 120
|
||||
TEST_ACK_TIMEOUT = 60
|
||||
@ -294,6 +294,7 @@ def build_dir(app_path: str, target: Optional[str], config: Optional[str]) -> st
|
||||
valid build directory
|
||||
"""
|
||||
check_dirs = []
|
||||
|
||||
if target is not None and config is not None:
|
||||
check_dirs.append(f'build_{target}_{config}')
|
||||
if target is not None:
|
||||
@ -312,8 +313,13 @@ def build_dir(app_path: str, target: Optional[str], config: Optional[str]) -> st
|
||||
'checking binary path: %s... missing... try another place', binary_path
|
||||
)
|
||||
|
||||
if config is not None and 'dummy' in config:
|
||||
logging.warning('no build dir valid for application: %s, config: %s. Skip test.', binary_path, config)
|
||||
return None
|
||||
|
||||
recommend_place = check_dirs[0]
|
||||
logging.error(
|
||||
f'no build dir valid. Please build the binary via "idf.py -B {recommend_place} build" and run pytest again'
|
||||
)
|
||||
|
||||
sys.exit(1)
|
||||
|
@ -1,7 +1,7 @@
|
||||
dependencies:
|
||||
idf: ">=4.1"
|
||||
espressif/esp-modbus:
|
||||
version: "^2.0"
|
||||
version: "^2.0.0-beta"
|
||||
override_path: "../../../../"
|
||||
mb_example_common:
|
||||
path: "../../../mb_example_common"
|
||||
|
@ -44,8 +44,13 @@
|
||||
#define TEST_INPUT_REG_START(field) (INPUT_OFFSET(field) >> 1)
|
||||
#define TEST_INPUT_REG_SIZE(field) (sizeof(((input_reg_params_t *)0)->field) >> 1)
|
||||
|
||||
#define TEST_VALUE 12345 // default test value
|
||||
#define TEST_ASCII_BIN 0xAAAAAAAA
|
||||
#define TEST_VALUE (12345) // default test value
|
||||
#define TEST_ASCII_BIN (0xAAAAAAAA)
|
||||
#define TEST_ARR_REG_SZ (58)
|
||||
#define TEST_HUMI_MIN (-40)
|
||||
#define TEST_HUMI_MAX (50)
|
||||
#define TEST_TEMP_MIN (0)
|
||||
#define TEST_TEMP_MAX (100)
|
||||
|
||||
// Options can be used as bit masks or parameter limits
|
||||
#define OPTS(min_val, max_val, step_val) { .opt1 = min_val, .opt2 = max_val, .opt3 = step_val }
|
||||
@ -107,31 +112,31 @@ const mb_parameter_descriptor_t device_parameters[] = {
|
||||
{ CID_INP_DATA_0, STR("Data_channel_0"), STR("Volts"), MB_DEVICE_ADDR1, MB_PARAM_INPUT,
|
||||
TEST_INPUT_REG_START(input_data0), TEST_INPUT_REG_SIZE(input_data0),
|
||||
INPUT_OFFSET(input_data0), PARAM_TYPE_FLOAT, 4,
|
||||
OPTS( 0, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
OPTS( TEST_TEMP_MIN, TEST_TEMP_MAX, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
{ CID_HOLD_DATA_0, STR("Humidity_1"), STR("%rH"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
|
||||
TEST_HOLD_REG_START(holding_data0), TEST_HOLD_REG_SIZE(holding_data0),
|
||||
HOLD_OFFSET(holding_data0), PARAM_TYPE_FLOAT, 4,
|
||||
OPTS( 0, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
OPTS( TEST_HUMI_MIN, TEST_HUMI_MAX, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
{ CID_INP_DATA_1, STR("Temperature_1"), STR("C"), MB_DEVICE_ADDR1, MB_PARAM_INPUT,
|
||||
TEST_INPUT_REG_START(input_data1), TEST_INPUT_REG_SIZE(input_data1),
|
||||
INPUT_OFFSET(input_data1), PARAM_TYPE_FLOAT, 4,
|
||||
OPTS( -40, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
OPTS( TEST_TEMP_MIN, TEST_TEMP_MAX, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
{ CID_HOLD_DATA_1, STR("Humidity_2"), STR("%rH"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
|
||||
TEST_HOLD_REG_START(holding_data1), TEST_HOLD_REG_SIZE(holding_data1),
|
||||
HOLD_OFFSET(holding_data1), PARAM_TYPE_FLOAT, 4,
|
||||
OPTS( 0, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
OPTS( TEST_HUMI_MIN, TEST_HUMI_MAX, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
{ CID_INP_DATA_2, STR("Temperature_2"), STR("C"), MB_DEVICE_ADDR1, MB_PARAM_INPUT,
|
||||
TEST_INPUT_REG_START(input_data2), TEST_INPUT_REG_SIZE(input_data2),
|
||||
INPUT_OFFSET(input_data2), PARAM_TYPE_FLOAT, 4,
|
||||
OPTS( -40, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
OPTS( TEST_TEMP_MIN, TEST_TEMP_MAX, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
{ CID_HOLD_DATA_2, STR("Humidity_3"), STR("%rH"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
|
||||
TEST_HOLD_REG_START(holding_data2), TEST_HOLD_REG_SIZE(holding_data2),
|
||||
HOLD_OFFSET(holding_data2), PARAM_TYPE_FLOAT, 4,
|
||||
OPTS( 0, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
OPTS( TEST_HUMI_MIN, TEST_HUMI_MAX, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
{ CID_HOLD_TEST_REG, STR("Test_regs"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
|
||||
TEST_HOLD_REG_START(test_regs), 58,
|
||||
HOLD_OFFSET(test_regs), PARAM_TYPE_ASCII, 116,
|
||||
OPTS( 0, 100, TEST_ASCII_BIN ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
TEST_HOLD_REG_START(test_regs), TEST_ARR_REG_SZ,
|
||||
HOLD_OFFSET(test_regs), PARAM_TYPE_ASCII, (TEST_ARR_REG_SZ * 2),
|
||||
OPTS( TEST_TEMP_MIN, TEST_TEMP_MAX, TEST_ASCII_BIN ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
{ CID_RELAY_P1, STR("RelayP1"), STR("on/off"), MB_DEVICE_ADDR1, MB_PARAM_COIL, 2, 6,
|
||||
COIL_OFFSET(coils_port0), PARAM_TYPE_U8, 1,
|
||||
OPTS( 0xAA, 0x15, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
@ -427,10 +432,9 @@ static void master_operation_func(void *arg)
|
||||
}
|
||||
|
||||
if (alarm_state) {
|
||||
ESP_LOGI(TAG, "Alarm triggered by cid #%d.",
|
||||
(int)param_descriptor->cid);
|
||||
ESP_LOGI(TAG, "Alarm triggered by cid #%u.", param_descriptor->cid);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Alarm is not triggered after %d retries.",
|
||||
ESP_LOGE(TAG, "Alarm is not triggered after %u retries.",
|
||||
MASTER_MAX_RETRY);
|
||||
}
|
||||
ESP_LOGI(TAG, "Destroy master...");
|
||||
|
@ -3,9 +3,9 @@
|
||||
|
||||
# Modbus Slave Example
|
||||
|
||||
This example demonstrates using the port of the FreeModbus stack on an ESP32 target where the ESP32 target is operating as a network slave. The example allows an external Modbus host to read/write device parameters on the ESP32 target using the Modbus protocol. The parameters accessible through Modbus are located in `mb_example_common/modbus_params.h\c` source/header files that users can update to add/remove their own custom parameters.
|
||||
This example demonstrates using the port of the esp-modbus stack on an ESP32 target where the ESP32 target is operating as a network slave. The example allows an external Modbus host to read/write device parameters on the ESP32 target using the Modbus protocol. The parameters accessible through Modbus are located in `mb_example_common/modbus_params.h\c` source/header files that users can update to add/remove their own custom parameters.
|
||||
These are represented in structures `holding_reg_params`, `input_reg_params`, `coil_reg_params`, `discrete_reg_params` for holding registers, input parameters, coils and discrete inputs accordingly. The app_main application demonstrates how to setup Modbus stack and use notifications about parameters change from host system.
|
||||
The FreeModbus stack located in `components/freemodbus` folder and contains the `/port` folder where the stack's port to the ESP32 is situated. There are some parameters of the port that can be configured in KConfig file to start stack correctly (See description below for more information).
|
||||
The FreeModbus stack located in `modbus` folder and supports the ESP32 targets. There are some parameters of the port that can be configured in KConfig file to start stack correctly (See description below for more information).
|
||||
|
||||
The slave example uses shared parameter structures defined in `examples/protocols/modbus/mb_example_common` folder.
|
||||
|
||||
@ -59,7 +59,7 @@ Note: Each target chip has different GPIO pins available for UART connection. Pl
|
||||
|
||||
Define the ```Modbus communiction mode``` for slave in Kconfig - CONFIG_MB_COMM_MODE (must be the same for master and slave application).
|
||||
Set ```Modbus slave address``` for the example application (by default for example script is set to 1).
|
||||
The communication parameters of freemodbus stack (Component config->Modbus configuration) allow to configure it appropriately but usually it is enough to use default settings.
|
||||
The communication parameters of esp-modbus stack (Component config->Modbus configuration) allow to configure it appropriately but usually it is enough to use default settings.
|
||||
See the help strings of parameters for more information.
|
||||
|
||||
### Setup external Modbus master software
|
||||
|
@ -98,7 +98,7 @@ menu "Modbus Example Configuration"
|
||||
|
||||
config MB_SLAVE_ADDR
|
||||
int "Modbus slave address"
|
||||
range 1 127
|
||||
range 1 247
|
||||
default 1
|
||||
help
|
||||
This is the Modbus slave address in the network.
|
||||
|
@ -1,7 +1,7 @@
|
||||
dependencies:
|
||||
idf: ">=4.1"
|
||||
espressif/esp-modbus:
|
||||
version: "^2.0"
|
||||
version: "^2.0.0-beta"
|
||||
override_path: "../../../../"
|
||||
mb_example_common:
|
||||
path: "../../../mb_example_common"
|
||||
|
@ -35,7 +35,7 @@
|
||||
|
||||
#define MB_PAR_INFO_GET_TOUT (10) // Timeout for get parameter info
|
||||
#define MB_CHAN_DATA_MAX_VAL (6)
|
||||
#define MB_CHAN_DATA_OFFSET (0.2f)
|
||||
#define MB_CHAN_DATA_OFFSET (1.2f)
|
||||
#define MB_READ_MASK (MB_EVENT_INPUT_REG_RD \
|
||||
| MB_EVENT_HOLDING_REG_RD \
|
||||
| MB_EVENT_DISCRETE_RD \
|
||||
@ -124,7 +124,7 @@ static void setup_reg_data(void)
|
||||
input_reg_params.input_data7 = 4.78;
|
||||
}
|
||||
|
||||
// An example application of Modbus slave. It is based on freemodbus stack.
|
||||
// An example application of Modbus slave. It is based on esp-modbus stack.
|
||||
// See deviceparams.h file for more information about assigned Modbus parameters.
|
||||
// These parameters can be accessed from main application and also can be changed
|
||||
// by external Modbus master host.
|
||||
|
@ -63,4 +63,9 @@ def test_modbus_serial_communication(config: str, dut: Tuple[ModbusTestDut, Modb
|
||||
dut_master.dut_test_start(dictionary=pattern_dict_master)
|
||||
|
||||
dut_slave.dut_check_errors()
|
||||
dut_master.dut_check_errors()
|
||||
dut_master.dut_check_errors()
|
||||
|
||||
@pytest.mark.multi_dut_modbus_generic
|
||||
@pytest.mark.parametrize('config', ['dummy_config'])
|
||||
def test_modbus_serial_generic() -> None:
|
||||
print('The generic serial example tests are not provided yet.')
|
@ -2,7 +2,7 @@ dependencies:
|
||||
idf:
|
||||
version: ">=4.1.0"
|
||||
espressif/esp-modbus:
|
||||
version: "^2.0"
|
||||
version: "^2.0.0-beta"
|
||||
override_path: "../../../../"
|
||||
espressif/mdns:
|
||||
version: "^1.0.0"
|
||||
|
@ -4,7 +4,7 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
// FreeModbus Master Example ESP32
|
||||
// esp-modbus Master Example ESP32
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/queue.h>
|
||||
@ -30,7 +30,7 @@
|
||||
#define MASTER_MAX_CIDS num_device_parameters
|
||||
|
||||
// Number of reading of parameters from slave
|
||||
#define MASTER_MAX_RETRY (30)
|
||||
#define MASTER_MAX_RETRY (10)
|
||||
|
||||
// Timeout to update cid over Modbus
|
||||
#define UPDATE_CIDS_TIMEOUT_MS (500)
|
||||
@ -54,8 +54,13 @@
|
||||
#define TEST_INPUT_REG_START(field) (INPUT_OFFSET(field) >> 1)
|
||||
#define TEST_INPUT_REG_SIZE(field) (sizeof(((input_reg_params_t *)0)->field) >> 1)
|
||||
|
||||
#define TEST_VALUE 12345 // default test value
|
||||
#define TEST_ASCII_BIN 0xAAAAAAAA
|
||||
#define TEST_VALUE (12345) // default test value
|
||||
#define TEST_ASCII_BIN (0xAAAAAAAA)
|
||||
#define TEST_ARR_REG_SZ (58)
|
||||
#define TEST_HUMI_MIN (-40)
|
||||
#define TEST_HUMI_MAX (50)
|
||||
#define TEST_TEMP_MIN (0)
|
||||
#define TEST_TEMP_MAX (100)
|
||||
|
||||
// Options can be used as bit masks or parameter limits
|
||||
#define OPTS(min_val, max_val, step_val) { .opt1 = min_val, .opt2 = max_val, .opt3 = step_val }
|
||||
@ -84,8 +89,9 @@ static const char *TAG = "MASTER_TEST";
|
||||
// Each address in the table is a index of TCP slave ip address in mb_communication_info_t::tcp_ip_addr table
|
||||
enum {
|
||||
MB_DEVICE_ADDR1 = 1, // Slave UID = 1
|
||||
MB_DEVICE_ADDR2 = 200,
|
||||
MB_DEVICE_ADDR3 = 35
|
||||
MB_DEVICE_ADDR2,
|
||||
MB_DEVICE_ADDR3,
|
||||
MB_DEVICE_COUNT = 3
|
||||
};
|
||||
|
||||
// Enumeration of all supported CIDs for device (used in parameter definition table)
|
||||
@ -135,31 +141,31 @@ const mb_parameter_descriptor_t device_parameters[] = {
|
||||
{ CID_INP_DATA_0, STR("Data_channel_0"), STR("Volts"), MB_DEVICE_ADDR1, MB_PARAM_INPUT,
|
||||
TEST_INPUT_REG_START(input_data0), TEST_INPUT_REG_SIZE(input_data0),
|
||||
INPUT_OFFSET(input_data0), PARAM_TYPE_FLOAT, 4,
|
||||
OPTS( 0, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
OPTS( TEST_TEMP_MIN, TEST_TEMP_MAX, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
{ CID_HOLD_DATA_0, STR("Humidity_1"), STR("%rH"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
|
||||
TEST_HOLD_REG_START(holding_data0), TEST_HOLD_REG_SIZE(holding_data0),
|
||||
HOLD_OFFSET(holding_data0), PARAM_TYPE_FLOAT, 4,
|
||||
OPTS( 0, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
OPTS( TEST_HUMI_MIN, TEST_HUMI_MAX, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
{ CID_INP_DATA_1, STR("Temperature_1"), STR("C"), MB_DEVICE_ADDR1, MB_PARAM_INPUT,
|
||||
TEST_INPUT_REG_START(input_data1), TEST_INPUT_REG_SIZE(input_data1),
|
||||
INPUT_OFFSET(input_data1), PARAM_TYPE_FLOAT, 4,
|
||||
OPTS( -40, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
OPTS( TEST_TEMP_MIN, TEST_TEMP_MAX, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
{ CID_HOLD_DATA_1, STR("Humidity_2"), STR("%rH"), MB_DEVICE_ADDR2, MB_PARAM_HOLDING,
|
||||
TEST_HOLD_REG_START(holding_data1), TEST_HOLD_REG_SIZE(holding_data1),
|
||||
HOLD_OFFSET(holding_data1), PARAM_TYPE_FLOAT, 4,
|
||||
OPTS( 0, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
OPTS( TEST_HUMI_MIN, TEST_HUMI_MAX, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
{ CID_INP_DATA_2, STR("Temperature_2"), STR("C"), MB_DEVICE_ADDR2, MB_PARAM_INPUT,
|
||||
TEST_INPUT_REG_START(input_data2), TEST_INPUT_REG_SIZE(input_data2),
|
||||
INPUT_OFFSET(input_data2), PARAM_TYPE_FLOAT, 4,
|
||||
OPTS( -40, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
OPTS( TEST_TEMP_MIN, TEST_TEMP_MAX, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
{ CID_HOLD_DATA_2, STR("Humidity_3"), STR("%rH"), MB_DEVICE_ADDR3, MB_PARAM_HOLDING,
|
||||
TEST_HOLD_REG_START(holding_data2), TEST_HOLD_REG_SIZE(holding_data2),
|
||||
HOLD_OFFSET(holding_data2), PARAM_TYPE_FLOAT, 4,
|
||||
OPTS( 0, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
OPTS( TEST_HUMI_MIN, TEST_HUMI_MAX, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
{ CID_HOLD_TEST_REG, STR("Test_regs"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
|
||||
TEST_HOLD_REG_START(test_regs), 58,
|
||||
HOLD_OFFSET(test_regs), PARAM_TYPE_ASCII, 116,
|
||||
OPTS( 0, 100, TEST_ASCII_BIN ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
TEST_HOLD_REG_START(test_regs), TEST_ARR_REG_SZ,
|
||||
HOLD_OFFSET(test_regs), PARAM_TYPE_ASCII, (TEST_ARR_REG_SZ * 2),
|
||||
OPTS( TEST_TEMP_MIN, TEST_TEMP_MAX, TEST_ASCII_BIN ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
{ CID_RELAY_P1, STR("RelayP1"), STR("on/off"), MB_DEVICE_ADDR1, MB_PARAM_COIL, 2, 6,
|
||||
COIL_OFFSET(coils_port0), PARAM_TYPE_U8, 1,
|
||||
OPTS( 0xAA, 0x15, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
@ -244,17 +250,27 @@ static void* master_handle = NULL;
|
||||
|
||||
const size_t ip_table_sz;
|
||||
|
||||
#if CONFIG_MB_SLAVE_IP_FROM_STDIN
|
||||
|
||||
// This table represents slave IP addresses that correspond to the short address field of the slave in device_parameters structure
|
||||
// Modbus TCP stack shall use these addresses to be able to connect and read parameters from slave
|
||||
char *slave_ip_address_table[] = {
|
||||
char* slave_ip_address_table[MB_DEVICE_COUNT + 1] = {
|
||||
#if CONFIG_MB_SLAVE_IP_FROM_STDIN
|
||||
"FROM_STDIN", // Address corresponds to MB_DEVICE_ADDR1 and set to predefined value by user
|
||||
//"FROM_STDIN", // Corresponds to characteristic MB_DEVICE_ADDR2
|
||||
//"FROM_STDIN", // Corresponds to characteristic MB_DEVICE_ADDR3
|
||||
"FROM_STDIN", // Address corresponds to MB_DEVICE_ADDR2 and set to predefined value by user
|
||||
"FROM_STDIN", // Address corresponds to MB_DEVICE_ADDR3 and set to predefined value by user
|
||||
NULL // End of table condition (must be included)
|
||||
#elif CONFIG_MB_MDNS_IP_RESOLVER
|
||||
// This is workaround for the test to use the same slave for all CIDs and ignore UID setting in the slave
|
||||
"01;mb_slave_tcp_01;1502",
|
||||
"02;mb_slave_tcp_01;1502",
|
||||
"03;mb_slave_tcp_01;1502",
|
||||
NULL // End of table condition (must be included)
|
||||
#endif
|
||||
};
|
||||
|
||||
const size_t ip_table_sz = (size_t)(sizeof(slave_ip_address_table) / sizeof(slave_ip_address_table[0]));
|
||||
|
||||
#if CONFIG_MB_SLAVE_IP_FROM_STDIN
|
||||
|
||||
// Scan IP address according to IPV settings
|
||||
char *master_scan_addr(int *index, char *buffer)
|
||||
{
|
||||
@ -262,20 +278,21 @@ char *master_scan_addr(int *index, char *buffer)
|
||||
int a[8] = {0};
|
||||
int buf_cnt = 0;
|
||||
#if !CONFIG_EXAMPLE_CONNECT_IPV6
|
||||
buf_cnt = sscanf(buffer, "IP%d="IPSTR, index, &a[0], &a[1], &a[2], &a[3]);
|
||||
buf_cnt = sscanf(buffer, "IP%d=" IPSTR, index, &a[0], &a[1], &a[2], &a[3]);
|
||||
if (buf_cnt == 5) {
|
||||
if (-1 == asprintf(&ip_str, IPSTR, a[0], a[1], a[2], a[3])) {
|
||||
if (-1 == asprintf(&ip_str, "%02x;" IPSTR, (int)(*index + 1), a[0], a[1], a[2], a[3])) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
#else
|
||||
buf_cnt = sscanf(buffer, "IP%d="IPV6STR, index, &a[0], &a[1], &a[2], &a[3], &a[4], &a[5], &a[6], &a[7]);
|
||||
if (buf_cnt == 9) {
|
||||
if (-1 == asprintf(&ip_str, IPV6STR, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7])) {
|
||||
if (-1 == asprintf(&ip_str, "%02x;" IPV6STR, (int)(*index + 1), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7])) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
printf("IP string: %s", ip_str);
|
||||
return ip_str;
|
||||
}
|
||||
|
||||
@ -327,19 +344,8 @@ static int master_get_slave_ip_stdin(char **addr_table)
|
||||
return ip_cnt;
|
||||
}
|
||||
|
||||
#elif CONFIG_MB_MDNS_IP_RESOLVER
|
||||
|
||||
char *slave_ip_address_table[] = {
|
||||
"01;mb_slave_tcp_01;502", // Corresponds to characteristic MB_DEVICE_ADDR1 "mb_slave_tcp_01"
|
||||
// "200;mb_slave_tcp_c8;1502", // Corresponds to characteristic MB_DEVICE_ADDR2 "mb_slave_tcp_C8"
|
||||
// "35;mb_slave_tcp_23;1502",
|
||||
NULL // End of table condition (must be included)
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
const size_t ip_table_sz = (size_t)(sizeof(slave_ip_address_table) / sizeof(slave_ip_address_table[0]));
|
||||
|
||||
static void master_destroy_slave_list(char **table, size_t ip_table_size)
|
||||
{
|
||||
for (int i = 0; ((i < ip_table_size) && table[i] != NULL); i++) {
|
||||
@ -572,7 +578,7 @@ static void master_operation_func(void *arg)
|
||||
MASTER_MAX_RETRY);
|
||||
}
|
||||
ESP_LOGI(TAG, "Destroy master...");
|
||||
vTaskDelay(100);
|
||||
vTaskDelay(1);
|
||||
}
|
||||
|
||||
static esp_err_t init_services(mb_tcp_addr_type_t ip_addr_type)
|
||||
@ -709,6 +715,7 @@ void app_main(void)
|
||||
.tcp_opts.uid = 0,
|
||||
.tcp_opts.start_disconnected = false,
|
||||
.tcp_opts.response_tout_ms = CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND,
|
||||
.tcp_opts.ip_netif_ptr = (void*)get_example_netif()
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(master_init(&tcp_master_config));
|
||||
|
@ -2,7 +2,7 @@
|
||||
# Modbus configuration
|
||||
#
|
||||
CONFIG_FMB_COMM_MODE_TCP_EN=y
|
||||
CONFIG_FMB_TCP_PORT_DEFAULT=502
|
||||
CONFIG_FMB_TCP_PORT_DEFAULT=1502
|
||||
CONFIG_FMB_TCP_CONNECTION_TOUT_SEC=20
|
||||
CONFIG_FMB_PORT_TASK_STACK_SIZE=4096
|
||||
CONFIG_FMB_PORT_TASK_PRIO=10
|
||||
@ -12,7 +12,6 @@ CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=3000
|
||||
CONFIG_FMB_MASTER_DELAY_MS_CONVERT=300
|
||||
CONFIG_FMB_TCP_UID_ENABLED=n
|
||||
CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y
|
||||
CONFIG_MB_MDNS_IP_RESOLVER=n
|
||||
CONFIG_MB_SLAVE_IP_FROM_STDIN=y
|
||||
CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y
|
||||
CONFIG_FMB_EXT_TYPE_SUPPORT=y
|
||||
|
@ -1,5 +1,5 @@
|
||||
CONFIG_FMB_COMM_MODE_TCP_EN=y
|
||||
CONFIG_FMB_TCP_PORT_DEFAULT=502
|
||||
CONFIG_FMB_TCP_PORT_DEFAULT=1502
|
||||
CONFIG_FMB_TCP_CONNECTION_TOUT_SEC=20
|
||||
CONFIG_FMB_PORT_TASK_STACK_SIZE=4096
|
||||
CONFIG_FMB_PORT_TASK_PRIO=10
|
||||
@ -9,7 +9,6 @@ CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=3000
|
||||
CONFIG_FMB_MASTER_DELAY_MS_CONVERT=300
|
||||
CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y
|
||||
CONFIG_FMB_TCP_UID_ENABLED=n
|
||||
CONFIG_MB_MDNS_IP_RESOLVER=n
|
||||
CONFIG_MB_SLAVE_IP_FROM_STDIN=y
|
||||
CONFIG_EXAMPLE_CONNECT_IPV6=n
|
||||
CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y
|
||||
|
@ -2,7 +2,7 @@
|
||||
# Modbus configuration
|
||||
#
|
||||
CONFIG_FMB_COMM_MODE_TCP_EN=y
|
||||
CONFIG_FMB_TCP_PORT_DEFAULT=502
|
||||
CONFIG_FMB_TCP_PORT_DEFAULT=1502
|
||||
CONFIG_FMB_TCP_CONNECTION_TOUT_SEC=20
|
||||
CONFIG_FMB_PORT_TASK_STACK_SIZE=4096
|
||||
CONFIG_FMB_PORT_TASK_PRIO=10
|
||||
@ -11,7 +11,6 @@ CONFIG_FMB_COMM_MODE_ASCII_EN=n
|
||||
CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=2000
|
||||
CONFIG_FMB_MASTER_DELAY_MS_CONVERT=300
|
||||
CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y
|
||||
CONFIG_MB_MDNS_IP_RESOLVER=n
|
||||
CONFIG_FMB_TCP_UID_ENABLED=n
|
||||
CONFIG_MB_SLAVE_IP_FROM_STDIN=y
|
||||
CONFIG_FMB_EXT_TYPE_SUPPORT=y
|
||||
|
@ -2,10 +2,9 @@
|
||||
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |
|
||||
|
||||
# Modbus Slave Example
|
||||
|
||||
This example demonstrates using of FreeModbus TCP slave stack port implementation for supported ESP32 target chips. The external Modbus host is able to read/write device parameters using Modbus protocol transport. The parameters accessible thorough Modbus are located in `mb_example_common/modbus_params.h\c` files and can be updated by user.
|
||||
The stack located in `modbus` folder and includes support for ESP32 target chips. There are some parameters that can be configured in KConfig file to start stack correctly (See description below for more information). The external Modbus host is able to read/write device parameters using Modbus protocol transport. The parameters accessible thorough Modbus are located in `mb_example_common/modbus_params.h\c` files and can be updated by user.
|
||||
These are represented in structures holding_reg_params, input_reg_params, coil_reg_params, discrete_reg_params for holding registers, input parameters, coils and discrete inputs accordingly. The app_main application demonstrates how to setup Modbus stack and use notifications about parameters change from host system.
|
||||
The FreeModbus stack located in `components/freemodbus` folder and contain `/port` folder inside which contains FreeModbus stack port for ESP32 target chips. There are some parameters that can be configured in KConfig file to start stack correctly (See description below for more information).
|
||||
|
||||
|
||||
The slave example uses shared parameter structures defined in ```examples/protocols/modbus/mb_example_common``` folder.
|
||||
|
||||
@ -28,7 +27,7 @@ idf.py menuconfig
|
||||
To configure the example to use Wi-Fi or Ethernet connection, open the project configuration menu and navigate to "Example Connection Configuration" menu. Select either "Wi-Fi" or "Ethernet" in the "Connect using" choice.
|
||||
Follow the instructions in `examples/common_components/protocol_examples_common` for further configuration.
|
||||
|
||||
The communication parameters of freemodbus stack (Component config->Modbus configuration) allow to configure it appropriately but usually it is enough to use default settings.
|
||||
The communication parameters of esp-modbus stack (Component config->Modbus configuration) allow to configure it appropriately but usually it is enough to use default settings.
|
||||
See the help strings of parameters for more information.
|
||||
|
||||
### Setup external Modbus master software
|
||||
|
@ -9,11 +9,4 @@ menu "Modbus Example Configuration"
|
||||
This is the Modbus slave address in the network.
|
||||
The address is used as an index to resolve slave ip address.
|
||||
|
||||
config MB_MDNS_IP_RESOLVER
|
||||
bool "Resolve slave addresses using mDNS service"
|
||||
default y
|
||||
help
|
||||
This option allows to use mDNS service to resolve IP addresses of the Modbus slaves.
|
||||
If the option is disabled the ip addresses of slaves are defined in static table.
|
||||
|
||||
endmenu
|
||||
|
@ -1,7 +1,7 @@
|
||||
dependencies:
|
||||
idf: ">=4.1"
|
||||
espressif/esp-modbus:
|
||||
version: "^2.0"
|
||||
version: "^2.0.0-beta"
|
||||
override_path: "../../../../"
|
||||
espressif/mdns:
|
||||
version: "^1.0.0"
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include "esp_system.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "mdns.h"
|
||||
#include "esp_netif.h"
|
||||
@ -28,7 +27,6 @@
|
||||
#include "modbus_params.h" // for modbus parameters structures
|
||||
|
||||
#define MB_TCP_PORT_NUMBER (CONFIG_FMB_TCP_PORT_DEFAULT)
|
||||
#define MB_MDNS_PORT (502)
|
||||
|
||||
// Defines below are used to define register start address for each type of Modbus registers
|
||||
#define HOLD_OFFSET(field) ((uint16_t)(offsetof(holding_reg_params_t, field) >> 1))
|
||||
@ -38,7 +36,11 @@
|
||||
#define MB_REG_INPUT_START_AREA0 (INPUT_OFFSET(input_data0)) // register offset input area 0
|
||||
#define MB_REG_INPUT_START_AREA1 (INPUT_OFFSET(input_data4)) // register offset input area 1
|
||||
#define MB_REG_HOLDING_START_AREA0 (HOLD_OFFSET(holding_data0))
|
||||
#define MB_REG_HOLDING_START_AREA0_SIZE ((size_t)((HOLD_OFFSET(holding_data4) - HOLD_OFFSET(holding_data0)) << 1))
|
||||
#define MB_REG_HOLDING_START_AREA1 (HOLD_OFFSET(holding_data4))
|
||||
#define MB_REG_HOLDING_START_AREA1_SIZE ((size_t)((HOLD_OFFSET(holding_area1_end) - HOLD_OFFSET(holding_data4)) << 1))
|
||||
#define MB_REG_HOLDING_START_AREA2 (HOLD_OFFSET(holding_u8_a))
|
||||
#define MB_REG_HOLDING_START_AREA2_SIZE ((size_t)((HOLD_OFFSET(holding_area2_end) - HOLD_OFFSET(holding_u8_a)) << 1))
|
||||
|
||||
#define MB_PAR_INFO_GET_TOUT (10) // Timeout for get parameter info
|
||||
#define MB_CHAN_DATA_MAX_VAL (10)
|
||||
@ -51,82 +53,13 @@
|
||||
#define MB_WRITE_MASK (MB_EVENT_HOLDING_REG_WR \
|
||||
| MB_EVENT_COILS_WR)
|
||||
#define MB_READ_WRITE_MASK (MB_READ_MASK | MB_WRITE_MASK)
|
||||
|
||||
#define MB_SLAVE_ADDR (CONFIG_MB_SLAVE_ADDR)
|
||||
#define MB_TEST_VALUE (12345.0)
|
||||
#define MB_SLAVE_ADDR (CONFIG_MB_SLAVE_ADDR)
|
||||
|
||||
static const char *TAG = "SLAVE_TEST";
|
||||
|
||||
static void *slave_handle = NULL;
|
||||
|
||||
#if CONFIG_MB_MDNS_IP_RESOLVER
|
||||
|
||||
#define MB_ID_BYTE0(id) ((uint8_t)(id))
|
||||
#define MB_ID_BYTE1(id) ((uint8_t)(((uint16_t)(id) >> 8) & 0xFF))
|
||||
#define MB_ID_BYTE2(id) ((uint8_t)(((uint32_t)(id) >> 16) & 0xFF))
|
||||
#define MB_ID_BYTE3(id) ((uint8_t)(((uint32_t)(id) >> 24) & 0xFF))
|
||||
|
||||
#define MB_ID2STR(id) MB_ID_BYTE0(id), MB_ID_BYTE1(id), MB_ID_BYTE2(id), MB_ID_BYTE3(id)
|
||||
|
||||
#if CONFIG_FMB_CONTROLLER_SLAVE_ID_SUPPORT
|
||||
#define MB_DEVICE_ID (uint32_t)CONFIG_FMB_CONTROLLER_SLAVE_ID
|
||||
#endif
|
||||
|
||||
#define MB_MDNS_INSTANCE(pref) pref"mb_slave_tcp"
|
||||
|
||||
// convert mac from binary format to string
|
||||
static inline char* gen_mac_str(const uint8_t* mac, char* pref, char* mac_str)
|
||||
{
|
||||
sprintf(mac_str, "%s%02X%02X%02X%02X%02X%02X", pref, MAC2STR(mac));
|
||||
return mac_str;
|
||||
}
|
||||
|
||||
static inline char* gen_id_str(char* service_name, char* slave_id_str)
|
||||
{
|
||||
sprintf(slave_id_str, "%s%02X%02X%02X%02X", service_name, MB_ID2STR(MB_DEVICE_ID));
|
||||
return slave_id_str;
|
||||
}
|
||||
|
||||
static inline char* gen_host_name_str(char* service_name, char* name)
|
||||
{
|
||||
sprintf(name, "%s_%02X", service_name, MB_SLAVE_ADDR);
|
||||
return name;
|
||||
}
|
||||
|
||||
static void start_mdns_service(void)
|
||||
{
|
||||
char temp_str[32] = {0};
|
||||
uint8_t sta_mac[6] = {0};
|
||||
ESP_ERROR_CHECK(esp_read_mac(sta_mac, ESP_MAC_WIFI_STA));
|
||||
char* hostname = gen_host_name_str(MB_MDNS_INSTANCE(""), temp_str);
|
||||
//initialize mDNS
|
||||
ESP_ERROR_CHECK(mdns_init());
|
||||
//set mDNS hostname (required if you want to advertise services)
|
||||
ESP_ERROR_CHECK(mdns_hostname_set(hostname));
|
||||
ESP_LOGI(TAG, "mdns hostname set to: [%s]", hostname);
|
||||
|
||||
//set default mDNS instance name
|
||||
ESP_ERROR_CHECK(mdns_instance_name_set(MB_MDNS_INSTANCE("esp32_")));
|
||||
|
||||
//structure with TXT records
|
||||
mdns_txt_item_t serviceTxtData[] = {
|
||||
{"board","esp32"}
|
||||
};
|
||||
|
||||
//initialize service
|
||||
ESP_ERROR_CHECK(mdns_service_add(hostname, "_modbus", "_tcp", MB_MDNS_PORT, serviceTxtData, 1));
|
||||
//add mac key string text item
|
||||
ESP_ERROR_CHECK(mdns_service_txt_item_set("_modbus", "_tcp", "mac", gen_mac_str(sta_mac, "\0", temp_str)));
|
||||
//add slave id key txt item
|
||||
ESP_ERROR_CHECK( mdns_service_txt_item_set("_modbus", "_tcp", "mb_id", gen_id_str("\0", temp_str)));
|
||||
}
|
||||
|
||||
static void stop_mdns_service(void)
|
||||
{
|
||||
mdns_free();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Set register values into known state
|
||||
static void setup_reg_data(void)
|
||||
{
|
||||
@ -150,6 +83,44 @@ static void setup_reg_data(void)
|
||||
holding_reg_params.holding_data6 = 7.79;
|
||||
holding_reg_params.holding_data7 = 8.80;
|
||||
|
||||
#if CONFIG_FMB_EXT_TYPE_SUPPORT
|
||||
mb_set_uint8_a((val_16_arr *)&holding_reg_params.holding_u8_a[0], (uint8_t)0x55);
|
||||
mb_set_uint8_a((val_16_arr *)&holding_reg_params.holding_u8_a[1], (uint8_t)0x55);
|
||||
mb_set_uint8_b((val_16_arr *)&holding_reg_params.holding_u8_b[0], (uint8_t)0x55);
|
||||
mb_set_uint8_b((val_16_arr *)&holding_reg_params.holding_u8_b[1], (uint8_t)0x55);
|
||||
mb_set_uint16_ab((val_16_arr *)&holding_reg_params.holding_u16_ab[1], (uint16_t)MB_TEST_VALUE);
|
||||
mb_set_uint16_ab((val_16_arr *)&holding_reg_params.holding_u16_ab[0], (uint16_t)MB_TEST_VALUE);
|
||||
mb_set_uint16_ba((val_16_arr *)&holding_reg_params.holding_u16_ba[0], (uint16_t)MB_TEST_VALUE);
|
||||
mb_set_uint16_ba((val_16_arr *)&holding_reg_params.holding_u16_ba[1], (uint16_t)MB_TEST_VALUE);
|
||||
|
||||
mb_set_float_abcd((val_32_arr *)&holding_reg_params.holding_float_abcd[0], (float)MB_TEST_VALUE);
|
||||
mb_set_float_abcd((val_32_arr *)&holding_reg_params.holding_float_abcd[1], (float)MB_TEST_VALUE);
|
||||
mb_set_float_cdab((val_32_arr *)&holding_reg_params.holding_float_cdab[0], (float)MB_TEST_VALUE);
|
||||
mb_set_float_cdab((val_32_arr *)&holding_reg_params.holding_float_cdab[1], (float)MB_TEST_VALUE);
|
||||
mb_set_float_badc((val_32_arr *)&holding_reg_params.holding_float_badc[0], (float)MB_TEST_VALUE);
|
||||
mb_set_float_badc((val_32_arr *)&holding_reg_params.holding_float_badc[1], (float)MB_TEST_VALUE);
|
||||
mb_set_float_dcba((val_32_arr *)&holding_reg_params.holding_float_dcba[0], (float)MB_TEST_VALUE);
|
||||
mb_set_float_dcba((val_32_arr *)&holding_reg_params.holding_float_dcba[1], (float)MB_TEST_VALUE);
|
||||
|
||||
mb_set_uint32_abcd((val_32_arr *)&holding_reg_params.holding_uint32_abcd[0], (uint32_t)MB_TEST_VALUE);
|
||||
mb_set_uint32_abcd((val_32_arr *)&holding_reg_params.holding_uint32_abcd[1], (uint32_t)MB_TEST_VALUE);
|
||||
mb_set_uint32_cdab((val_32_arr *)&holding_reg_params.holding_uint32_cdab[0], (uint32_t)MB_TEST_VALUE);
|
||||
mb_set_uint32_cdab((val_32_arr *)&holding_reg_params.holding_uint32_cdab[1], (uint32_t)MB_TEST_VALUE);
|
||||
mb_set_uint32_badc((val_32_arr *)&holding_reg_params.holding_uint32_badc[0], (uint32_t)MB_TEST_VALUE);
|
||||
mb_set_uint32_badc((val_32_arr *)&holding_reg_params.holding_uint32_badc[1], (uint32_t)MB_TEST_VALUE);
|
||||
mb_set_uint32_dcba((val_32_arr *)&holding_reg_params.holding_uint32_dcba[0], (uint32_t)MB_TEST_VALUE);
|
||||
mb_set_uint32_dcba((val_32_arr *)&holding_reg_params.holding_uint32_dcba[1], (uint32_t)MB_TEST_VALUE);
|
||||
|
||||
mb_set_double_abcdefgh((val_64_arr *)&holding_reg_params.holding_double_abcdefgh[0], (double)MB_TEST_VALUE);
|
||||
mb_set_double_abcdefgh((val_64_arr *)&holding_reg_params.holding_double_abcdefgh[1], (double)MB_TEST_VALUE);
|
||||
mb_set_double_hgfedcba((val_64_arr *)&holding_reg_params.holding_double_hgfedcba[0], (double)MB_TEST_VALUE);
|
||||
mb_set_double_hgfedcba((val_64_arr *)&holding_reg_params.holding_double_hgfedcba[1], (double)MB_TEST_VALUE);
|
||||
mb_set_double_ghefcdab((val_64_arr *)&holding_reg_params.holding_double_ghefcdab[0], (double)MB_TEST_VALUE);
|
||||
mb_set_double_ghefcdab((val_64_arr *)&holding_reg_params.holding_double_ghefcdab[1], (double)MB_TEST_VALUE);
|
||||
mb_set_double_badcfehg((val_64_arr *)&holding_reg_params.holding_double_badcfehg[0], (double)MB_TEST_VALUE);
|
||||
mb_set_double_badcfehg((val_64_arr *)&holding_reg_params.holding_double_badcfehg[1], (double)MB_TEST_VALUE);
|
||||
#endif
|
||||
|
||||
coil_reg_params.coils_port0 = 0x55;
|
||||
coil_reg_params.coils_port1 = 0xAA;
|
||||
|
||||
@ -157,7 +128,6 @@ static void setup_reg_data(void)
|
||||
input_reg_params.input_data1 = 2.34;
|
||||
input_reg_params.input_data2 = 3.56;
|
||||
input_reg_params.input_data3 = 4.78;
|
||||
|
||||
input_reg_params.input_data4 = 1.12;
|
||||
input_reg_params.input_data5 = 2.34;
|
||||
input_reg_params.input_data6 = 3.56;
|
||||
@ -175,24 +145,25 @@ static void slave_operation_func(void *arg)
|
||||
for(;holding_reg_params.holding_data0 < MB_CHAN_DATA_MAX_VAL;) {
|
||||
// Check for read/write events of Modbus master for certain events
|
||||
(void)mbc_slave_check_event(slave_handle, MB_READ_WRITE_MASK);
|
||||
// Get parameter information from parameter queue
|
||||
ESP_ERROR_CHECK(mbc_slave_get_param_info(slave_handle, ®_info, MB_PAR_INFO_GET_TOUT));
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(mbc_slave_get_param_info(slave_handle, ®_info, MB_PAR_INFO_GET_TOUT));
|
||||
const char* rw_str = (reg_info.type & MB_READ_MASK) ? "READ" : "WRITE";
|
||||
// Filter events and process them accordingly
|
||||
if(reg_info.type & (MB_EVENT_HOLDING_REG_WR | MB_EVENT_HOLDING_REG_RD)) {
|
||||
ESP_LOGI(TAG, "HOLDING %s (%" PRIu32 " us), ADDR:%u, TYPE:%u, INST_ADDR:0x%" PRIx32 ", SIZE:%u",
|
||||
rw_str,
|
||||
reg_info.time_stamp,
|
||||
(unsigned)reg_info.mb_offset,
|
||||
(unsigned)reg_info.type,
|
||||
(uint32_t)reg_info.address,
|
||||
(unsigned)reg_info.size);
|
||||
// Get parameter information from parameter queue
|
||||
ESP_LOGI(TAG, "HOLDING %s (%u us), ADDR:%u, TYPE:%u, INST_ADDR:0x%.4x, SIZE:%u",
|
||||
rw_str,
|
||||
(unsigned)reg_info.time_stamp,
|
||||
(unsigned)reg_info.mb_offset,
|
||||
(unsigned)reg_info.type,
|
||||
(int)reg_info.address,
|
||||
(unsigned)reg_info.size);
|
||||
if (reg_info.address == (uint8_t*)&holding_reg_params.holding_data0)
|
||||
{
|
||||
(void)mbc_slave_unlock(slave_handle);
|
||||
holding_reg_params.holding_data0 += MB_CHAN_DATA_OFFSET;
|
||||
if (holding_reg_params.holding_data0 >= (MB_CHAN_DATA_MAX_VAL - MB_CHAN_DATA_OFFSET)) {
|
||||
coil_reg_params.coils_port1 = 0xFF;
|
||||
ESP_LOGI(TAG, "Riched maximum value");
|
||||
}
|
||||
(void)mbc_slave_unlock(slave_handle);
|
||||
}
|
||||
@ -218,9 +189,10 @@ static void slave_operation_func(void *arg)
|
||||
(unsigned)reg_info.type,
|
||||
(uint32_t)reg_info.address,
|
||||
(unsigned)reg_info.size);
|
||||
if (coil_reg_params.coils_port1 == 0xFF) break;
|
||||
|
||||
|
||||
if (coil_reg_params.coils_port1 == 0xFF) {
|
||||
ESP_LOGI(TAG, "Stop polling.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Destroy of Modbus controller on alarm
|
||||
@ -249,10 +221,6 @@ static esp_err_t init_services(void)
|
||||
TAG,
|
||||
"esp_event_loop_create_default fail, returns(0x%x).",
|
||||
(int)result);
|
||||
#if CONFIG_MB_MDNS_IP_RESOLVER
|
||||
// Start mdns service and register device
|
||||
start_mdns_service();
|
||||
#endif
|
||||
// This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
|
||||
// Read "Establishing Wi-Fi or Ethernet Connection" section in
|
||||
// examples/protocols/README.md for more information about this function.
|
||||
@ -295,9 +263,6 @@ static esp_err_t destroy_services(void)
|
||||
TAG,
|
||||
"nvs_flash_deinit fail, returns(0x%x).",
|
||||
(int)err);
|
||||
#if CONFIG_MB_MDNS_IP_RESOLVER
|
||||
stop_mdns_service();
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -338,6 +303,19 @@ static esp_err_t slave_init(mb_communication_info_t *pcomm_info)
|
||||
"mbc_slave_set_descriptor fail, returns(0x%x).",
|
||||
(int)err);
|
||||
|
||||
#if CONFIG_FMB_EXT_TYPE_SUPPORT
|
||||
// The extended parameters register area
|
||||
reg_area.type = MB_PARAM_HOLDING;
|
||||
reg_area.start_offset = MB_REG_HOLDING_START_AREA2;
|
||||
reg_area.address = (void*)&holding_reg_params.holding_u8_a;
|
||||
reg_area.size = MB_REG_HOLDING_START_AREA2_SIZE;
|
||||
err = mbc_slave_set_descriptor(slave_handle, reg_area);
|
||||
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
TAG,
|
||||
"mbc_slave_set_descriptor fail, returns(0x%x).",
|
||||
(int)err);
|
||||
#endif
|
||||
|
||||
// Initialization of Input Registers area
|
||||
reg_area.type = MB_PARAM_INPUT;
|
||||
reg_area.start_offset = MB_REG_INPUT_START_AREA0;
|
||||
@ -403,7 +381,7 @@ static esp_err_t slave_destroy(void)
|
||||
return err;
|
||||
}
|
||||
|
||||
// An example application of Modbus slave. It is based on freemodbus stack.
|
||||
// An example application of Modbus slave. It is based on esp-modbus stack.
|
||||
// See deviceparams.h file for more information about assigned Modbus parameters.
|
||||
// These parameters can be accessed from main application and also can be changed
|
||||
// by external Modbus master host.
|
||||
@ -412,7 +390,6 @@ void app_main(void)
|
||||
ESP_ERROR_CHECK(init_services());
|
||||
|
||||
// Set UART log level
|
||||
|
||||
esp_log_level_set(TAG, ESP_LOG_INFO);
|
||||
|
||||
mb_communication_info_t tcp_slave_config = {
|
||||
|
@ -2,7 +2,7 @@
|
||||
# Modbus configuration
|
||||
#
|
||||
CONFIG_FMB_COMM_MODE_TCP_EN=y
|
||||
CONFIG_FMB_TCP_PORT_DEFAULT=502
|
||||
CONFIG_FMB_TCP_PORT_DEFAULT=1502
|
||||
CONFIG_FMB_TCP_CONNECTION_TOUT_SEC=20
|
||||
CONFIG_FMB_PORT_TASK_STACK_SIZE=4096
|
||||
CONFIG_FMB_PORT_TASK_PRIO=10
|
||||
@ -10,9 +10,9 @@ CONFIG_FMB_COMM_MODE_RTU_EN=n
|
||||
CONFIG_FMB_COMM_MODE_ASCII_EN=n
|
||||
CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=3000
|
||||
CONFIG_FMB_MASTER_DELAY_MS_CONVERT=300
|
||||
CONFIG_FMB_EXT_TYPE_SUPPORT=y
|
||||
CONFIG_FMB_TCP_UID_ENABLED=n
|
||||
CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y
|
||||
CONFIG_MB_MDNS_IP_RESOLVER=n
|
||||
CONFIG_MB_SLAVE_ADDR=1
|
||||
CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y
|
||||
CONFIG_EXAMPLE_CONNECT_IPV6=n
|
||||
|
@ -1,15 +1,15 @@
|
||||
CONFIG_FMB_COMM_MODE_TCP_EN=y
|
||||
CONFIG_FMB_TCP_PORT_DEFAULT=502
|
||||
CONFIG_FMB_TCP_PORT_DEFAULT=1502
|
||||
CONFIG_FMB_TCP_CONNECTION_TOUT_SEC=20
|
||||
CONFIG_FMB_PORT_TASK_STACK_SIZE=4096
|
||||
CONFIG_FMB_PORT_TASK_PRIO=10
|
||||
CONFIG_FMB_COMM_MODE_RTU_EN=n
|
||||
CONFIG_FMB_COMM_MODE_ASCII_EN=n
|
||||
CONFIG_FMB_EXT_TYPE_SUPPORT=y
|
||||
CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=3000
|
||||
CONFIG_FMB_MASTER_DELAY_MS_CONVERT=300
|
||||
CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y
|
||||
CONFIG_FMB_TCP_UID_ENABLED=n
|
||||
CONFIG_MB_MDNS_IP_RESOLVER=n
|
||||
CONFIG_MB_SLAVE_ADDR=1
|
||||
CONFIG_EXAMPLE_CONNECT_IPV6=n
|
||||
CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y
|
||||
|
@ -2,7 +2,7 @@
|
||||
# Modbus configuration
|
||||
#
|
||||
CONFIG_FMB_COMM_MODE_TCP_EN=y
|
||||
CONFIG_FMB_TCP_PORT_DEFAULT=502
|
||||
CONFIG_FMB_TCP_PORT_DEFAULT=1502
|
||||
CONFIG_FMB_TCP_CONNECTION_TOUT_SEC=20
|
||||
CONFIG_FMB_PORT_TASK_STACK_SIZE=4096
|
||||
CONFIG_FMB_PORT_TASK_PRIO=10
|
||||
@ -10,7 +10,6 @@ CONFIG_FMB_COMM_MODE_RTU_EN=n
|
||||
CONFIG_FMB_COMM_MODE_ASCII_EN=n
|
||||
CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=2000
|
||||
CONFIG_FMB_MASTER_DELAY_MS_CONVERT=300
|
||||
CONFIG_MB_MDNS_IP_RESOLVER=n
|
||||
CONFIG_FMB_TCP_UID_ENABLED=n
|
||||
CONFIG_MB_SLAVE_ADDR=1
|
||||
CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y
|
||||
|
@ -12,8 +12,8 @@ import pytest
|
||||
|
||||
from conftest import ModbusTestDut, Stages
|
||||
|
||||
pattern_dict_slave = {Stages.STACK_IPV4: (r'I \([0-9]+\) example_connect: - IPv4 address: ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})'),
|
||||
Stages.STACK_IPV6: (r'I \([0-9]+\) example_connect: - IPv6 address: (([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4})'),
|
||||
pattern_dict_slave = {Stages.STACK_IPV4: (r'I \([0-9]+\) example_[a-z]+: - IPv4 address: ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})'),
|
||||
Stages.STACK_IPV6: (r'I \([0-9]+\) example_[a-z]+: - IPv6 address: (([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4})'),
|
||||
Stages.STACK_INIT: (r'I \(([0-9]+)\) MB_TCP_SLAVE_PORT: (Protocol stack initialized).'),
|
||||
Stages.STACK_CONNECT: (r'I\s\(([0-9]+)\) MB_TCP_SLAVE_PORT: Socket \(#[0-9]+\), accept client connection from address: '
|
||||
r'([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})'),
|
||||
@ -23,8 +23,8 @@ pattern_dict_slave = {Stages.STACK_IPV4: (r'I \([0-9]+\) example_connect: - IPv4
|
||||
Stages.STACK_PAR_FAIL: (r'E \(([0-9]+)\) SLAVE_TEST: Response time exceeds configured [0-9]+ [ms], ignore packet'),
|
||||
Stages.STACK_DESTROY: (r'I\s\(([0-9]+)\) SLAVE_TEST: (Modbus controller destroyed).')}
|
||||
|
||||
pattern_dict_master = {Stages.STACK_IPV4: (r'I \([0-9]+\) example_connect: - IPv4 address: ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})'),
|
||||
Stages.STACK_IPV6: (r'I \([0-9]+\) example_connect: - IPv6 address: (([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4})'),
|
||||
pattern_dict_master = {Stages.STACK_IPV4: (r'I \([0-9]+\) example_[a-z]+: - IPv4 address: ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})'),
|
||||
Stages.STACK_IPV6: (r'I \([0-9]+\) example_[a-z]+: - IPv6 address: (([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4})'),
|
||||
Stages.STACK_INIT: (r'I \(([0-9]+)\) MASTER_TEST: (Modbus master stack initialized)'),
|
||||
Stages.STACK_CONNECT: (r'I\s\(([0-9]+)\) MB_TCP_MASTER_PORT: (Connected [0-9]+ slaves), start polling'),
|
||||
Stages.STACK_START: (r'I \(([0-9]+)\) MASTER_TEST: (Start modbus test)'),
|
||||
@ -66,4 +66,9 @@ def test_modbus_tcp_communication(dut: Tuple[ModbusTestDut, ModbusTestDut]) -> N
|
||||
dut_master.dut_test_start(dictionary=pattern_dict_master)
|
||||
|
||||
dut_slave.dut_check_errors()
|
||||
dut_master.dut_check_errors()
|
||||
dut_master.dut_check_errors()
|
||||
|
||||
@pytest.mark.multi_dut_modbus_generic
|
||||
@pytest.mark.parametrize('config', ['dummy_config'])
|
||||
def test_modbus_tcp_generic() -> None:
|
||||
print('The generic tcp example tests are not provided yet.')
|
@ -1,4 +1,4 @@
|
||||
version: "2.0.0"
|
||||
version: "2.0.0-beta.1"
|
||||
description: ESP-MODBUS is the official Modbus library for Espressif SoCs.
|
||||
url: https://github.com/espressif/esp-modbus
|
||||
dependencies:
|
||||
@ -9,3 +9,8 @@ files:
|
||||
- "docs"
|
||||
- "test/**/*"
|
||||
- "test"
|
||||
- "pytest_embedded_log/**/*"
|
||||
- "build*/**/*"
|
||||
- "**/*.zip"
|
||||
- "__pycache__/**/*"
|
||||
|
||||
|
24
linker.lf
24
linker.lf
@ -2,22 +2,22 @@
|
||||
archive: libesp-modbus.a
|
||||
entries:
|
||||
* (default)
|
||||
port_event: mb_port_evt_set_err_type (noflash_text)
|
||||
port_event: mb_port_evt_post (noflash_text)
|
||||
port_event: mb_port_event_set_err_type (noflash_text)
|
||||
port_event: mb_port_event_post (noflash_text)
|
||||
port_other: lock_obj (noflash_text)
|
||||
port_other: unlock_obj (noflash_text)
|
||||
|
||||
if FMB_TIMER_USE_ISR_DISPATCH_METHOD = y:
|
||||
# tcp_master: mbm_tcp_transp_tmr_expired (noflash_text)
|
||||
tcp_slave: mbs_tcp_transp_tmr_expired (noflash_text)
|
||||
port_tcp_slave: mbm_port_timer_expired (noflash_text)
|
||||
# tcp_master: mbm_tcp_transp_timer_expired (noflash_text)
|
||||
tcp_slave: mbs_tcp_transp_timer_expired (noflash_text)
|
||||
# port_tcp_slave: mbs_port_timer_expired (noflash_text)
|
||||
port_tcp_master: mbm_port_timer_expired (noflash_text)
|
||||
port_timer: timer_alarm_cb (noflash_text)
|
||||
port_timer: mb_port_set_cur_tmr_mode (noflash_text)
|
||||
port_timer: mb_port_get_cur_tmr_mode (noflash_text)
|
||||
port_timer: mb_port_tmr_disable (noflash_text)
|
||||
ascii_master: mbm_ascii_transp_tmr_expired (noflash_text)
|
||||
ascii_slave: mbs_ascii_transp_tmr_1s_expired (noflash_text)
|
||||
rtu_master: mbm_rtu_transp_tmr_35_expired (noflash_text)
|
||||
rtu_slave: mbs_rtu_transp_tmr_35_expired (noflash_text)
|
||||
port_timer: mb_port_set_cur_timer_mode (noflash_text)
|
||||
port_timer: mb_port_get_cur_timer_mode (noflash_text)
|
||||
port_timer: mb_port_timer_disable (noflash_text)
|
||||
ascii_master: mbm_ascii_transp_timer_expired (noflash_text)
|
||||
ascii_slave: mbs_ascii_transp_timer_expired (noflash_text)
|
||||
rtu_master: mbm_rtu_transp_timer_expired (noflash_text)
|
||||
rtu_slave: mbs_rtu_transp_timer_expired (noflash_text)
|
||||
|
||||
|
@ -278,7 +278,7 @@ static esp_err_t mbc_slave_send_param_info(void *ctx, mb_event_group_t par_type,
|
||||
par_info.mb_offset = mb_offset;
|
||||
BaseType_t status = xQueueSend(mbs_opts->notification_queue_handle, &par_info, MB_PAR_INFO_TOUT);
|
||||
if (pdTRUE == status) {
|
||||
ESP_LOGD(TAG, "Queue send parameter info (type, address, size): %d, 0x%" PRIu32 ", %d",
|
||||
ESP_LOGD(TAG, "Queue send parameter info (type, address, size): %d, 0x%" PRIx32 ", %d",
|
||||
(int)par_type, (uint32_t)par_address, (int)par_size);
|
||||
error = ESP_OK;
|
||||
} else if (errQUEUE_FULL == status) {
|
||||
|
@ -405,7 +405,7 @@ esp_err_t mbc_master_set_parameter_with(void *ctx, uint16_t cid, uint8_t uid, ui
|
||||
* - MB_ENOERR: Read write is successful
|
||||
* - MB_ENOREG: The argument is incorrect
|
||||
*/
|
||||
mb_err_enum_t mbc_reg_holding_master_cb(mb_base_t *inst, uint8_t *reg_buffer, uint16_t address, uint16_t n_regs, mb_reg_mode_enum_t mode);
|
||||
mb_err_enum_t mbc_reg_holding_master_cb(mb_base_t *inst, uint8_t *reg_buffer, uint16_t address, uint16_t n_regs, mb_reg_mode_enum_t mode) __attribute__ ((weak));
|
||||
|
||||
/**
|
||||
* @brief Input register read/write callback function
|
||||
@ -419,7 +419,7 @@ mb_err_enum_t mbc_reg_holding_master_cb(mb_base_t *inst, uint8_t *reg_buffer, ui
|
||||
* - MB_ENOERR: Read write is successful
|
||||
* - MB_ENOREG: The argument is incorrect
|
||||
*/
|
||||
mb_err_enum_t mbc_reg_input_master_cb(mb_base_t *inst, uint8_t *reg_buffer, uint16_t address, uint16_t n_regs);
|
||||
mb_err_enum_t mbc_reg_input_master_cb(mb_base_t *inst, uint8_t *reg_buffer, uint16_t address, uint16_t n_regs) __attribute__ ((weak));
|
||||
|
||||
/**
|
||||
* @brief Discrete register read/write callback function
|
||||
@ -433,7 +433,7 @@ mb_err_enum_t mbc_reg_input_master_cb(mb_base_t *inst, uint8_t *reg_buffer, uint
|
||||
* - MB_ENOERR: Read write is successful
|
||||
* - MB_ENOREG: The argument is incorrect
|
||||
*/
|
||||
mb_err_enum_t mbc_reg_discrete_master_cb(mb_base_t *inst, uint8_t *reg_buffer, uint16_t address, uint16_t n_discrete);
|
||||
mb_err_enum_t mbc_reg_discrete_master_cb(mb_base_t *inst, uint8_t *reg_buffer, uint16_t address, uint16_t n_discrete) __attribute__ ((weak));
|
||||
|
||||
/**
|
||||
* @brief Coil register read/write callback function
|
||||
@ -448,7 +448,7 @@ mb_err_enum_t mbc_reg_discrete_master_cb(mb_base_t *inst, uint8_t *reg_buffer, u
|
||||
* - MB_ENOERR: Read write is successful
|
||||
* - MB_ENOREG: The argument is incorrect
|
||||
*/
|
||||
mb_err_enum_t mbc_reg_coils_master_cb(mb_base_t *inst, uint8_t *reg_buffer, uint16_t address, uint16_t n_coils, mb_reg_mode_enum_t mode);
|
||||
mb_err_enum_t mbc_reg_coils_master_cb(mb_base_t *inst, uint8_t *reg_buffer, uint16_t address, uint16_t n_coils, mb_reg_mode_enum_t mode) __attribute__ ((weak));
|
||||
|
||||
/**
|
||||
* @brief The helper function to set data of parameters according to its type
|
||||
|
@ -163,18 +163,6 @@ esp_err_t mbc_slave_start(void *ctx);
|
||||
*/
|
||||
esp_err_t mbc_slave_stop(void *ctx);
|
||||
|
||||
/**
|
||||
* @brief Set Modbus communication parameters for the controller
|
||||
*
|
||||
* @param[in] ctx context pointer of the initialized modbus interface
|
||||
* @param comm_info Communication parameters structure.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_ARG Incorrect parameter data
|
||||
*/
|
||||
esp_err_t mbc_slave_setup(void *ctx, void *comm_info);
|
||||
|
||||
/**
|
||||
* @brief Wait for specific event on parameter change.
|
||||
*
|
||||
@ -226,7 +214,7 @@ esp_err_t mbc_slave_set_descriptor(void *ctx, mb_register_area_descriptor_t desc
|
||||
* - MB_ENOERR: Read write is successful
|
||||
* - MB_ENOREG: The argument is incorrect
|
||||
*/
|
||||
mb_err_enum_t mbc_reg_holding_slave_cb(mb_base_t *inst, uint8_t *reg_buffer, uint16_t address, uint16_t n_regs, mb_reg_mode_enum_t mode);
|
||||
mb_err_enum_t mbc_reg_holding_slave_cb(mb_base_t *inst, uint8_t *reg_buffer, uint16_t address, uint16_t n_regs, mb_reg_mode_enum_t mode) __attribute__ ((weak));
|
||||
|
||||
/**
|
||||
* @brief Input register read/write callback function
|
||||
@ -240,7 +228,7 @@ mb_err_enum_t mbc_reg_holding_slave_cb(mb_base_t *inst, uint8_t *reg_buffer, uin
|
||||
* - MB_ENOERR: Read write is successful
|
||||
* - MB_ENOREG: The argument is incorrect
|
||||
*/
|
||||
mb_err_enum_t mbc_reg_input_slave_cb(mb_base_t *inst, uint8_t *reg_buffer, uint16_t address, uint16_t n_regs);
|
||||
mb_err_enum_t mbc_reg_input_slave_cb(mb_base_t *inst, uint8_t *reg_buffer, uint16_t address, uint16_t n_regs) __attribute__ ((weak));
|
||||
|
||||
/**
|
||||
* @brief Discrete register read/write callback function
|
||||
@ -254,7 +242,7 @@ mb_err_enum_t mbc_reg_input_slave_cb(mb_base_t *inst, uint8_t *reg_buffer, uint1
|
||||
* - MB_ENOERR: Read write is successful
|
||||
* - MB_ENOREG: The argument is incorrect
|
||||
*/
|
||||
mb_err_enum_t mbc_reg_discrete_slave_cb(mb_base_t *inst, uint8_t *reg_buffer, uint16_t address, uint16_t n_discrete);
|
||||
mb_err_enum_t mbc_reg_discrete_slave_cb(mb_base_t *inst, uint8_t *reg_buffer, uint16_t address, uint16_t n_discrete) __attribute__ ((weak));
|
||||
|
||||
/**
|
||||
* @brief Coil register read/write callback function
|
||||
@ -269,7 +257,7 @@ mb_err_enum_t mbc_reg_discrete_slave_cb(mb_base_t *inst, uint8_t *reg_buffer, ui
|
||||
* - MB_ENOERR: Read write is successful
|
||||
* - MB_ENOREG: The argument is incorrect
|
||||
*/
|
||||
mb_err_enum_t mbc_reg_coils_slave_cb(mb_base_t *inst, uint8_t *reg_buffer, uint16_t address, uint16_t n_coils, mb_reg_mode_enum_t mode);
|
||||
mb_err_enum_t mbc_reg_coils_slave_cb(mb_base_t *inst, uint8_t *reg_buffer, uint16_t address, uint16_t n_coils, mb_reg_mode_enum_t mode) __attribute__ ((weak));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -71,7 +71,6 @@ typedef struct
|
||||
iface_check_event_fp check_event; /*!< Interface method check_event */
|
||||
iface_get_param_info_fp get_param_info; /*!< Interface method get_param_info */
|
||||
iface_mbs_set_descriptor_fp set_descriptor; /*!< Interface method set_descriptor */
|
||||
|
||||
} mbs_controller_iface_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -114,6 +114,7 @@ static esp_err_t mbc_serial_master_delete(void *ctx)
|
||||
ESP_ERR_INVALID_STATE, TAG, "mb stack stop failure.");
|
||||
}
|
||||
|
||||
mbm_iface->is_active = false;
|
||||
vTaskDelete(mbm_opts->task_handle);
|
||||
mbm_opts->task_handle = NULL;
|
||||
vEventGroupDelete(mbm_opts->event_group_handle);
|
||||
@ -160,9 +161,8 @@ static esp_err_t mbc_serial_master_send_request(void *ctx, mb_param_request_t *r
|
||||
mb_err_enum_t mb_error = MB_EBUSY;
|
||||
esp_err_t error = ESP_FAIL;
|
||||
|
||||
if (mb_port_evt_res_take(mbm_controller_iface->mb_base->port_obj, pdMS_TO_TICKS(MB_MAX_RESP_DELAY_MS)))
|
||||
if (mb_port_event_res_take(mbm_controller_iface->mb_base->port_obj, pdMS_TO_TICKS(MB_MAX_RESP_DELAY_MS)))
|
||||
{
|
||||
|
||||
uint8_t mb_slave_addr = request->slave_addr;
|
||||
uint8_t mb_command = request->command;
|
||||
uint16_t mb_offset = request->reg_start;
|
||||
@ -172,7 +172,7 @@ static esp_err_t mbc_serial_master_send_request(void *ctx, mb_param_request_t *r
|
||||
mbm_opts->reg_buffer_ptr = (uint8_t *)data_ptr;
|
||||
mbm_opts->reg_buffer_size = mb_size;
|
||||
|
||||
mb_port_evt_res_release(mbm_controller_iface->mb_base->port_obj);
|
||||
mb_port_event_res_release(mbm_controller_iface->mb_base->port_obj);
|
||||
|
||||
// Calls appropriate request function to send request and waits response
|
||||
switch (mb_command)
|
||||
|
@ -133,6 +133,7 @@ static esp_err_t mbc_serial_slave_delete(void *ctx)
|
||||
}
|
||||
}
|
||||
|
||||
mbs_iface->is_active = false;
|
||||
vTaskDelete(mbs_opts->task_handle);
|
||||
vEventGroupDelete(mbs_opts->event_group_handle);
|
||||
vQueueDelete(mbs_opts->notification_queue_handle);
|
||||
|
@ -24,7 +24,7 @@
|
||||
#include "port_tcp_common.h"
|
||||
#include "port_tcp_master.h"
|
||||
|
||||
#include "mb_common.h" // for mb types definition
|
||||
#include "mb_common.h" // for mb types definition
|
||||
#include "mb_config.h"
|
||||
#include "mb_proto.h"
|
||||
#include "mb_port_types.h"
|
||||
@ -63,8 +63,7 @@ static void mbc_tcp_master_conn_done_cb(void *ctx)
|
||||
{
|
||||
mb_master_options_t *mbm_opts = MB_MASTER_GET_OPTS(ctx);
|
||||
|
||||
ESP_LOGI(TAG, "Modbus controller interface callback.");
|
||||
|
||||
ESP_LOGI(TAG, "mb controller connection done.");
|
||||
EventBits_t flag = xEventGroupSetBits(mbm_opts->event_group_handle,
|
||||
(EventBits_t)MB_EVENT_STACK_CONNECTED);
|
||||
MB_RETURN_ON_FALSE((flag & MB_EVENT_STACK_CONNECTED),
|
||||
@ -171,7 +170,7 @@ static esp_err_t mbc_tcp_master_send_request(void *ctx, mb_param_request_t *requ
|
||||
mb_err_enum_t mb_error = MB_EBUSY;
|
||||
esp_err_t error = ESP_FAIL;
|
||||
|
||||
if (mb_port_evt_res_take(mbm_controller_iface->mb_base->port_obj, pdMS_TO_TICKS(MB_MAX_RESP_DELAY_MS))) {
|
||||
if (mb_port_event_res_take(mbm_controller_iface->mb_base->port_obj, pdMS_TO_TICKS(MB_MAX_RESP_DELAY_MS))) {
|
||||
uint8_t mb_slave_addr = request->slave_addr;
|
||||
uint8_t mb_command = request->command;
|
||||
uint16_t mb_offset = request->reg_start;
|
||||
@ -181,7 +180,7 @@ static esp_err_t mbc_tcp_master_send_request(void *ctx, mb_param_request_t *requ
|
||||
mbm_opts->reg_buffer_ptr = (uint8_t *)data_ptr;
|
||||
mbm_opts->reg_buffer_size = mb_size;
|
||||
|
||||
mb_port_evt_res_release(mbm_controller_iface->mb_base->port_obj);
|
||||
mb_port_event_res_release(mbm_controller_iface->mb_base->port_obj);
|
||||
|
||||
// Calls appropriate request function to send request and waits response
|
||||
switch(mb_command) {
|
||||
@ -561,7 +560,7 @@ static esp_err_t mbc_tcp_master_set_parameter_with(void *ctx, uint16_t cid, uint
|
||||
"mb can not send request for cid #%d with uid=%d.",
|
||||
(unsigned)reg_info.cid, (int)uid);
|
||||
if (request.slave_addr != MB_SLAVE_ADDR_PLACEHOLDER) {
|
||||
ESP_LOGW(TAG, "%s: override uid %d = %d for cid(%u)",
|
||||
ESP_LOGD(TAG, "%s: override uid %d = %d for cid(%u)",
|
||||
__FUNCTION__, (int)request.slave_addr, (int)uid, (unsigned)reg_info.cid);
|
||||
}
|
||||
request.slave_addr = uid; // override the UID
|
||||
@ -613,10 +612,12 @@ static esp_err_t mbc_tcp_master_delete(void *ctx)
|
||||
pdFALSE,
|
||||
pdMS_TO_TICKS(mbm_opts->comm_opts.tcp_opts.response_tout_ms));
|
||||
if (mbm_iface->is_active || (status & MB_EVENT_STACK_STARTED)) {
|
||||
ESP_LOGW(TAG, "mb stack is active, try to disable.");
|
||||
ESP_LOGD(TAG, "mb stack is active, try to disable.");
|
||||
MB_RETURN_ON_FALSE((mbc_tcp_master_stop(ctx) == ESP_OK),
|
||||
ESP_ERR_INVALID_STATE, TAG, "mb stack stop failure.");
|
||||
}
|
||||
|
||||
mbm_iface->is_active = false;
|
||||
vTaskDelete(mbm_opts->task_handle);
|
||||
mbm_opts->task_handle = NULL;
|
||||
vEventGroupDelete(mbm_opts->event_group_handle);
|
||||
@ -704,7 +705,7 @@ esp_err_t mbc_tcp_master_create(mb_communication_info_t *config, void **ctx)
|
||||
// Check communication options
|
||||
mb_tcp_opts_t tcp_opts = (mb_tcp_opts_t)config->tcp_opts;
|
||||
MB_RETURN_ON_FALSE((tcp_opts.ip_addr_table), ESP_ERR_INVALID_ARG, TAG, "mb ip table address is incorrect.");
|
||||
MB_RETURN_ON_FALSE(((tcp_opts.mode == MB_TCP) || (tcp_opts.mode == MB_UDP)),
|
||||
MB_RETURN_ON_FALSE((tcp_opts.mode == MB_TCP),
|
||||
ESP_ERR_INVALID_ARG, TAG, "mb transport protocol is incorrect.");
|
||||
MB_RETURN_ON_FALSE(((tcp_opts.addr_type == MB_IPV6) || (tcp_opts.addr_type == MB_IPV4)),
|
||||
ESP_ERR_INVALID_ARG, TAG, "mb ip address type is incorrect.");
|
||||
|
@ -127,6 +127,7 @@ static esp_err_t mbc_tcp_slave_delete(void *ctx)
|
||||
ESP_ERR_INVALID_STATE, TAG, "mb stack stop failure.");
|
||||
}
|
||||
|
||||
mbs_iface->is_active = false;
|
||||
vTaskDelete(mbs_opts->task_handle);
|
||||
vEventGroupDelete(mbs_opts->event_group_handle);
|
||||
vQueueDelete(mbs_opts->notification_queue_handle);
|
||||
|
@ -82,7 +82,7 @@ mb_err_enum_t mbm_rq_read_coils(mb_base_t *inst, uint8_t snd_addr, uint16_t coil
|
||||
if (snd_addr > MB_ADDRESS_MAX) {
|
||||
return MB_EINVAL;
|
||||
}
|
||||
if (!mb_port_evt_res_take(inst->port_obj, tout)) {
|
||||
if (!mb_port_event_res_take(inst->port_obj, tout)) {
|
||||
return MB_EBUSY;
|
||||
}
|
||||
inst->get_send_buf(inst, &mb_frame_ptr);
|
||||
@ -98,8 +98,8 @@ mb_err_enum_t mbm_rq_read_coils(mb_base_t *inst, uint8_t snd_addr, uint16_t coil
|
||||
|
||||
inst->set_send_len(inst, MB_PDU_SIZE_MIN + MB_PDU_REQ_READ_SIZE);
|
||||
|
||||
(void)mb_port_evt_post(inst->port_obj, EVENT(EV_FRAME_TRANSMIT | EV_TRANS_START) );
|
||||
return mb_port_evt_wait_req_finish(inst->port_obj);
|
||||
(void)mb_port_event_post(inst->port_obj, EVENT(EV_FRAME_TRANSMIT | EV_TRANS_START) );
|
||||
return mb_port_event_wait_req_finish(inst->port_obj);
|
||||
}
|
||||
|
||||
mb_exception_t mbm_fn_read_coils(mb_base_t *inst, uint8_t *frame_ptr, uint16_t *len_buf)
|
||||
@ -182,7 +182,7 @@ mb_err_enum_t mbm_rq_write_coil(mb_base_t *inst, uint8_t snd_addr, uint16_t coil
|
||||
if ((coil_data != 0xFF00) && (coil_data != 0x0000)) {
|
||||
return MB_EINVAL;
|
||||
}
|
||||
if (!mb_port_evt_res_take(inst->port_obj, tout)) {
|
||||
if (!mb_port_event_res_take(inst->port_obj, tout)) {
|
||||
return MB_EBUSY;
|
||||
}
|
||||
inst->get_send_buf(inst, &mb_frame_ptr);
|
||||
@ -199,8 +199,8 @@ mb_err_enum_t mbm_rq_write_coil(mb_base_t *inst, uint8_t snd_addr, uint16_t coil
|
||||
|
||||
inst->set_send_len(inst, (MB_PDU_SIZE_MIN + MB_PDU_REQ_WRITE_SIZE));
|
||||
|
||||
(void)mb_port_evt_post(inst->port_obj, EVENT(EV_FRAME_TRANSMIT | EV_TRANS_START) );
|
||||
return mb_port_evt_wait_req_finish(inst->port_obj);
|
||||
(void)mb_port_event_post(inst->port_obj, EVENT(EV_FRAME_TRANSMIT | EV_TRANS_START) );
|
||||
return mb_port_event_wait_req_finish(inst->port_obj);
|
||||
}
|
||||
|
||||
mb_exception_t mbm_fn_write_coil(mb_base_t *inst, uint8_t *frame_ptr, uint16_t *len_buf)
|
||||
@ -249,7 +249,7 @@ mb_err_enum_t mbm_rq_write_multi_coils(mb_base_t *inst, uint8_t snd_addr, uint16
|
||||
if (coil_num > MB_PDU_REQ_WRITE_MUL_COILCNT_MAX) {
|
||||
return MB_EINVAL;
|
||||
}
|
||||
if (!mb_port_evt_res_take(inst->port_obj, tout)) {
|
||||
if (!mb_port_event_res_take(inst->port_obj, tout)) {
|
||||
return MB_EBUSY;
|
||||
}
|
||||
inst->get_send_buf(inst, &mb_frame_ptr);
|
||||
@ -278,8 +278,8 @@ mb_err_enum_t mbm_rq_write_multi_coils(mb_base_t *inst, uint8_t snd_addr, uint16
|
||||
|
||||
inst->set_send_len(inst, (MB_PDU_SIZE_MIN + MB_PDU_REQ_WRITE_MUL_SIZE_MIN + byte_cnt));
|
||||
|
||||
(void)mb_port_evt_post(inst->port_obj, EVENT(EV_FRAME_TRANSMIT | EV_TRANS_START) );
|
||||
return mb_port_evt_wait_req_finish(inst->port_obj);
|
||||
(void)mb_port_event_post(inst->port_obj, EVENT(EV_FRAME_TRANSMIT | EV_TRANS_START) );
|
||||
return mb_port_event_wait_req_finish(inst->port_obj);
|
||||
}
|
||||
|
||||
mb_exception_t mbm_fn_write_multi_coils(mb_base_t *inst, uint8_t *frame_ptr, uint16_t *len_buf)
|
||||
@ -332,4 +332,4 @@ mb_exception_t mbm_fn_write_multi_coils(mb_base_t *inst, uint8_t *frame_ptr, uin
|
||||
}
|
||||
|
||||
#endif // #if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED > 0
|
||||
#endif // #if MB_MASTER_RTU_ENABLED > 0 || MB_MASTER_ASCII_ENABLED > 0
|
||||
#endif // #if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
|
||||
|
@ -64,7 +64,7 @@ mb_err_enum_t mbm_rq_read_discrete_inputs(mb_base_t *inst, uint8_t snd_addr, uin
|
||||
if (!inst || (snd_addr > MB_ADDRESS_MAX)) {
|
||||
return MB_EINVAL;
|
||||
}
|
||||
if (!mb_port_evt_res_take(inst->port_obj, tout)) {
|
||||
if (!mb_port_event_res_take(inst->port_obj, tout)) {
|
||||
return MB_EBUSY;
|
||||
}
|
||||
inst->get_send_buf(inst, &mb_frame_ptr);
|
||||
@ -80,8 +80,8 @@ mb_err_enum_t mbm_rq_read_discrete_inputs(mb_base_t *inst, uint8_t snd_addr, uin
|
||||
|
||||
inst->set_send_len(inst, MB_PDU_SIZE_MIN + MB_PDU_REQ_READ_SIZE);
|
||||
|
||||
(void)mb_port_evt_post(inst->port_obj, EVENT(EV_FRAME_TRANSMIT | EV_TRANS_START) );
|
||||
return mb_port_evt_wait_req_finish(inst->port_obj);
|
||||
(void)mb_port_event_post(inst->port_obj, EVENT(EV_FRAME_TRANSMIT | EV_TRANS_START) );
|
||||
return mb_port_event_wait_req_finish(inst->port_obj);
|
||||
}
|
||||
|
||||
mb_exception_t mbm_fn_read_discrete_inputs(mb_base_t *inst, uint8_t *frame_ptr, uint16_t *len_buf)
|
||||
|
@ -94,7 +94,7 @@ mb_err_enum_t mbm_rq_write_holding_reg(mb_base_t *inst, uint8_t snd_addr, uint16
|
||||
{
|
||||
return MB_EINVAL;
|
||||
}
|
||||
if (!mb_port_evt_res_take(inst->port_obj, tout))
|
||||
if (!mb_port_event_res_take(inst->port_obj, tout))
|
||||
{
|
||||
return MB_EBUSY;
|
||||
}
|
||||
@ -111,8 +111,8 @@ mb_err_enum_t mbm_rq_write_holding_reg(mb_base_t *inst, uint8_t snd_addr, uint16
|
||||
|
||||
inst->set_send_len(inst, MB_PDU_SIZE_MIN + MB_PDU_REQ_READ_SIZE);
|
||||
|
||||
(void)mb_port_evt_post(inst->port_obj, EVENT(EV_FRAME_TRANSMIT | EV_TRANS_START));
|
||||
return mb_port_evt_wait_req_finish(inst->port_obj);
|
||||
(void)mb_port_event_post(inst->port_obj, EVENT(EV_FRAME_TRANSMIT | EV_TRANS_START));
|
||||
return mb_port_event_wait_req_finish(inst->port_obj);
|
||||
}
|
||||
|
||||
mb_exception_t mbm_fn_write_holding_reg(mb_base_t *inst, uint8_t *frame_ptr, uint16_t *len_buf)
|
||||
@ -170,7 +170,7 @@ mb_err_enum_t mbm_rq_write_multi_holding_reg(mb_base_t *inst, uint8_t snd_addr,
|
||||
{
|
||||
return MB_EINVAL;
|
||||
}
|
||||
if (!mb_port_evt_res_take(inst->port_obj, tout))
|
||||
if (!mb_port_event_res_take(inst->port_obj, tout))
|
||||
{
|
||||
return MB_EBUSY;
|
||||
}
|
||||
@ -198,8 +198,8 @@ mb_err_enum_t mbm_rq_write_multi_holding_reg(mb_base_t *inst, uint8_t snd_addr,
|
||||
|
||||
inst->set_send_len(inst, (MB_PDU_SIZE_MIN + MB_PDU_REQ_WRITE_MUL_SIZE_MIN + 2 * reg_num));
|
||||
|
||||
(void)mb_port_evt_post(inst->port_obj, EVENT(EV_FRAME_TRANSMIT | EV_TRANS_START));
|
||||
return mb_port_evt_wait_req_finish(inst->port_obj);
|
||||
(void)mb_port_event_post(inst->port_obj, EVENT(EV_FRAME_TRANSMIT | EV_TRANS_START));
|
||||
return mb_port_event_wait_req_finish(inst->port_obj);
|
||||
}
|
||||
|
||||
mb_exception_t mbm_fn_write_multi_holding_reg(mb_base_t *inst, uint8_t *frame_ptr, uint16_t *len_buf)
|
||||
@ -272,7 +272,7 @@ mb_err_enum_t mbm_rq_read_holding_reg(mb_base_t *inst, uint8_t snd_addr, uint16_
|
||||
{
|
||||
return MB_EINVAL;
|
||||
}
|
||||
if (!mb_port_evt_res_take(inst->port_obj, tout))
|
||||
if (!mb_port_event_res_take(inst->port_obj, tout))
|
||||
{
|
||||
return MB_EBUSY;
|
||||
}
|
||||
@ -289,8 +289,8 @@ mb_err_enum_t mbm_rq_read_holding_reg(mb_base_t *inst, uint8_t snd_addr, uint16_
|
||||
|
||||
inst->set_send_len(inst, (MB_PDU_SIZE_MIN + MB_PDU_REQ_READ_SIZE));
|
||||
|
||||
(void)mb_port_evt_post(inst->port_obj, EVENT(EV_FRAME_TRANSMIT | EV_TRANS_START));
|
||||
return mb_port_evt_wait_req_finish(inst->port_obj);
|
||||
(void)mb_port_event_post(inst->port_obj, EVENT(EV_FRAME_TRANSMIT | EV_TRANS_START));
|
||||
return mb_port_event_wait_req_finish(inst->port_obj);
|
||||
}
|
||||
|
||||
mb_exception_t mbm_fn_read_holding_reg(mb_base_t *inst, uint8_t *frame_ptr, uint16_t *len_buf)
|
||||
@ -376,7 +376,7 @@ mb_err_enum_t mbm_rq_rw_multi_holding_reg(mb_base_t *inst, uint8_t snd_addr, uin
|
||||
{
|
||||
return MB_EINVAL;
|
||||
}
|
||||
if (!mb_port_evt_res_take(inst->port_obj, tout))
|
||||
if (!mb_port_event_res_take(inst->port_obj, tout))
|
||||
{
|
||||
return MB_EBUSY;
|
||||
}
|
||||
@ -408,8 +408,8 @@ mb_err_enum_t mbm_rq_rw_multi_holding_reg(mb_base_t *inst, uint8_t snd_addr, uin
|
||||
|
||||
inst->set_send_len(inst, (MB_PDU_SIZE_MIN + MB_PDU_REQ_READWRITE_SIZE_MIN + 2 * wr_reg_num));
|
||||
|
||||
(void)mb_port_evt_post(inst->port_obj, EVENT(EV_FRAME_TRANSMIT | EV_TRANS_START));
|
||||
return mb_port_evt_wait_req_finish(inst->port_obj);
|
||||
(void)mb_port_event_post(inst->port_obj, EVENT(EV_FRAME_TRANSMIT | EV_TRANS_START));
|
||||
return mb_port_event_wait_req_finish(inst->port_obj);
|
||||
}
|
||||
|
||||
mb_exception_t mbm_fn_rw_multi_holding_regs(mb_base_t *inst, uint8_t *frame_ptr, uint16_t *len_buf)
|
||||
|
@ -67,7 +67,7 @@ mb_err_enum_t mbm_rq_read_inp_reg(mb_base_t *inst, uint8_t snd_addr, uint16_t re
|
||||
if (!inst || (snd_addr > MB_ADDRESS_MAX)) {
|
||||
return MB_EINVAL;
|
||||
}
|
||||
if (!mb_port_evt_res_take(inst->port_obj, tout)) {
|
||||
if (!mb_port_event_res_take(inst->port_obj, tout)) {
|
||||
return MB_EBUSY;
|
||||
}
|
||||
inst->get_send_buf(inst, &mb_frame_ptr);
|
||||
@ -83,8 +83,8 @@ mb_err_enum_t mbm_rq_read_inp_reg(mb_base_t *inst, uint8_t snd_addr, uint16_t re
|
||||
|
||||
inst->set_send_len(inst, MB_PDU_SIZE_MIN + MB_PDU_REQ_READ_SIZE);
|
||||
|
||||
(void)mb_port_evt_post(inst->port_obj, EVENT(EV_FRAME_TRANSMIT | EV_TRANS_START) );
|
||||
return mb_port_evt_wait_req_finish(inst->port_obj);
|
||||
(void)mb_port_event_post(inst->port_obj, EVENT(EV_FRAME_TRANSMIT | EV_TRANS_START) );
|
||||
return mb_port_event_wait_req_finish(inst->port_obj);
|
||||
}
|
||||
|
||||
mb_exception_t mbm_fn_read_inp_reg(mb_base_t *inst, uint8_t *frame_ptr, uint16_t *len_buf)
|
||||
|
@ -55,36 +55,53 @@ extern "C" {
|
||||
(void)log_tag; \
|
||||
if (!(a)) { \
|
||||
ESP_LOGE(log_tag, "%s(%d): " format, __FUNCTION__, __LINE__ __VA_OPT__(,) __VA_ARGS__); \
|
||||
ret = (err_code); \
|
||||
ret = (err_code); \
|
||||
goto goto_tag; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
|
||||
#define MB_CAT_BUF_SIZE (100)
|
||||
|
||||
#define MB_STR_CAT(pref, message) (__extension__( \
|
||||
{ \
|
||||
char buf##__FUNCTION__##__LINE__[MB_CAT_BUF_SIZE]; \
|
||||
strncpy(&(buf##__FUNCTION__##__LINE__)[0], pref, (MB_CAT_BUF_SIZE - 1)); \
|
||||
strncat((buf##__FUNCTION__##__LINE__), message, (MB_CAT_BUF_SIZE - 1)); \
|
||||
(&((buf##__FUNCTION__##__LINE__)[0])); \
|
||||
} \
|
||||
))
|
||||
|
||||
#define MB_OBJ_FMT "%p"
|
||||
|
||||
#define MB_OBJ_PTR(inst) (__extension__( \
|
||||
{ \
|
||||
assert(inst); \
|
||||
(((obj_descr_t*)(inst))->parent); \
|
||||
} \
|
||||
#define MB_GET_OBJ_CTX(pinst, type, base) (__extension__( \
|
||||
{ \
|
||||
assert(pinst); \
|
||||
((type *)__containerof(pinst, type, base)); \
|
||||
} \
|
||||
))
|
||||
|
||||
#define MB_BASE2PORT(inst) (__extension__( \
|
||||
{ \
|
||||
assert(inst); \
|
||||
(((mb_base_t *)inst)->port_obj); \
|
||||
} \
|
||||
#define MB_OBJ(pinst) (__extension__( \
|
||||
{ \
|
||||
assert(pinst); \
|
||||
((typeof(pinst))(pinst)); \
|
||||
} \
|
||||
))
|
||||
|
||||
#define CAT_BUF_SIZE (100)
|
||||
#define MB_STR_CAT(pref, message) (__extension__( \
|
||||
{ \
|
||||
static char string_buf[CAT_BUF_SIZE]; \
|
||||
strncpy(string_buf, pref, (CAT_BUF_SIZE - 1)); \
|
||||
strncat(string_buf, message, (CAT_BUF_SIZE - 1)); \
|
||||
} \
|
||||
#define MB_OBJ_PARENT(pinst) (__extension__( \
|
||||
{ \
|
||||
assert(pinst); \
|
||||
(((obj_descr_t*)(pinst))->parent); \
|
||||
} \
|
||||
))
|
||||
|
||||
#define MB_BASE2PORT(pinst) (__extension__( \
|
||||
{ \
|
||||
assert(pinst); \
|
||||
assert(((mb_base_t *)pinst)->port_obj); \
|
||||
(((mb_base_t *)pinst)->port_obj); \
|
||||
} \
|
||||
))
|
||||
|
||||
typedef struct mb_base_t mb_base_t;
|
||||
|
@ -5,6 +5,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "stdatomic.h"
|
||||
#include "mb_config.h"
|
||||
#include "mb_types.h"
|
||||
|
||||
@ -12,6 +13,8 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define MB_ATTR_WEAK __attribute__ ((weak))
|
||||
|
||||
typedef enum _mb_comm_mode mb_mode_type_t;
|
||||
|
||||
#if (CONFIG_FMB_COMM_MODE_ASCII_EN || CONFIG_FMB_COMM_MODE_RTU_EN)
|
||||
@ -60,42 +63,44 @@ struct _port_tcp_opts {
|
||||
mb_addr_type_t addr_type; /*!< Modbus address type */
|
||||
void *ip_addr_table; /*!< Modbus address or table for connection */
|
||||
void *ip_netif_ptr; /*!< Modbus network interface */
|
||||
bool start_disconnected; /*!< do not wait connection to all nodes before polling */
|
||||
char *dns_name; /*!< Modbus node DNS name */
|
||||
bool start_disconnected; /*!< (Master only option) do not wait for connection to all nodes before polling */
|
||||
};
|
||||
|
||||
typedef struct _port_tcp_opts mb_tcp_opts_t;
|
||||
|
||||
// The common object descriptor struture (common for mb, transport, port objects)
|
||||
struct _obj_descr {
|
||||
char *parent_name;
|
||||
char *obj_name;
|
||||
void *parent;
|
||||
uint32_t inst_index;
|
||||
bool is_master;
|
||||
char *parent_name; /*!< Name of the parent (base) object */
|
||||
char *obj_name; /*!< Name of the object */
|
||||
void *parent; /*!< Pointer to the parent (base) object */
|
||||
uint32_t inst_index; /*!< The consicutive index of the object instance */
|
||||
bool is_master; /*!< The current object is master or slave (false) */
|
||||
};
|
||||
|
||||
typedef struct _obj_descr obj_descr_t;
|
||||
|
||||
typedef enum _mb_sock_state {
|
||||
MB_SOCK_STATE_UNDEF = 0x0000,
|
||||
MB_SOCK_STATE_CLOSED,
|
||||
MB_SOCK_STATE_READY,
|
||||
MB_SOCK_STATE_OPENED,
|
||||
MB_SOCK_STATE_RESOLVED,
|
||||
MB_SOCK_STATE_CONNECTING,
|
||||
MB_SOCK_STATE_CONNECTED
|
||||
MB_SOCK_STATE_UNDEF = 0x0000, /*!< Default init state */
|
||||
MB_SOCK_STATE_CLOSED, /*!< Node is closed */
|
||||
MB_SOCK_STATE_READY, /*!< Node is ready for communication */
|
||||
MB_SOCK_STATE_OPENED, /*!< Node is opened */
|
||||
MB_SOCK_STATE_RESOLVED, /*!< Node address is resolved */
|
||||
MB_SOCK_STATE_CONNECTING, /*!< Node connection is in progress */
|
||||
MB_SOCK_STATE_CONNECTED, /*!< Node is connected */
|
||||
MB_SOCK_STATE_ACCEPTED /*!< Slave node accepted the connection */
|
||||
} mb_sock_state_t;
|
||||
|
||||
typedef struct _uid_info {
|
||||
uint16_t index; /*!< index of the address info */
|
||||
int fd; /*!< slave global FD for VFS (reserved) */
|
||||
char *node_name_str; /*!< node name string (host name of slave to resolve) */
|
||||
char *ip_addr_str; /*!< represents the IP address of the slave */
|
||||
int fd; /*!< node global FD for VFS (reserved) */
|
||||
char *node_name_str; /*!< node name string (host name of node to resolve) */
|
||||
char *ip_addr_str; /*!< represents the IP address of the node */
|
||||
mb_addr_type_t addr_type; /*!< type of IP address */
|
||||
uint16_t uid; /*!< slave unit ID (UID) field for MBAP frame */
|
||||
uint16_t port; /*!< slave port number */
|
||||
uint16_t uid; /*!< node unit ID (UID) field for MBAP frame */
|
||||
uint16_t port; /*!< node port number */
|
||||
mb_comm_mode_t proto; /*!< protocol type */
|
||||
mb_sock_state_t state; /*!< slave state */
|
||||
_Atomic mb_sock_state_t state; /*!< node state */
|
||||
void *inst; /*!< pointer to linked instance */
|
||||
} mb_uid_info_t;
|
||||
|
||||
|
@ -33,7 +33,7 @@ typedef enum _mb_commands_enum
|
||||
MB_FUNC_DIAG_GET_COM_EVENT_CNT = ( 11 ),
|
||||
MB_FUNC_DIAG_GET_COM_EVENT_LOG = ( 12 ),
|
||||
MB_FUNC_OTHER_REPORT_SLAVEID = ( 17 ),
|
||||
MB_FUNC_ERROR = ( 128u ),
|
||||
MB_FUNC_ERROR = ( 128U ),
|
||||
} mb_commands_t;
|
||||
|
||||
/* ----------------------- Type definitions ---------------------------------*/
|
||||
|
@ -133,7 +133,7 @@ typedef enum
|
||||
MB_TMODE_T35, /*!< Master receive frame T3.5 timeout. */
|
||||
MB_TMODE_RESPOND_TIMEOUT, /*!< Master wait respond for slave. */
|
||||
MB_TMODE_CONVERT_DELAY /*!< Master sent broadcast , then delay sometime.*/
|
||||
} mb_tmr_mode_enum_t;
|
||||
} mb_timer_mode_enum_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -175,7 +175,7 @@ mb_err_enum_t mbm_ascii_create(mb_serial_opts_t *ser_opts, void **in_out_obj)
|
||||
mbm_obj->cur_state = STATE_DISABLED;
|
||||
transp_obj->get_tx_frm(transp_obj, (uint8_t **)&mbm_obj->snd_frame);
|
||||
transp_obj->get_rx_frm(transp_obj, (uint8_t **)&mbm_obj->rcv_frame);
|
||||
mbm_obj->base.port_obj = transp_obj->port_obj; // binding of the modbus object with port objet
|
||||
mbm_obj->base.port_obj = transp_obj->port_obj; // binding of the modbus object with port object
|
||||
mbm_obj->base.transp_obj = transp_obj;
|
||||
*in_out_obj = (void *)&(mbm_obj->base);
|
||||
ESP_LOGD(TAG, "created object %s", mbm_obj->base.descr.parent_name);
|
||||
@ -253,10 +253,10 @@ error:
|
||||
|
||||
mb_err_enum_t mbm_delete(mb_base_t *inst)
|
||||
{
|
||||
mbm_object_t *mbm_obj = __containerof(inst, mbm_object_t, base);
|
||||
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);;
|
||||
mb_err_enum_t status = MB_ENOERR;
|
||||
if (mbm_obj->cur_state == STATE_DISABLED) {
|
||||
if (mbm_obj->base.transp_obj->frm_delete) {
|
||||
if (MB_OBJ(mbm_obj->base.transp_obj)->frm_delete) {
|
||||
// call destructor of the transport object
|
||||
mbm_obj->base.transp_obj->frm_delete(inst->transp_obj);
|
||||
}
|
||||
@ -275,13 +275,13 @@ mb_err_enum_t mbm_delete(mb_base_t *inst)
|
||||
|
||||
mb_err_enum_t mbm_enable(mb_base_t *inst)
|
||||
{
|
||||
mbm_object_t *mbm_obj = __containerof(inst, mbm_object_t, base);
|
||||
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);;
|
||||
mb_err_enum_t status = MB_ENOERR;
|
||||
CRITICAL_SECTION(inst->lock)
|
||||
{
|
||||
if (mbm_obj->cur_state == STATE_DISABLED) {
|
||||
/* Activate the protocol stack. */
|
||||
mbm_obj->base.transp_obj->frm_start(mbm_obj->base.transp_obj);
|
||||
MB_OBJ(mbm_obj->base.transp_obj)->frm_start(mbm_obj->base.transp_obj);
|
||||
mbm_obj->cur_state = STATE_ENABLED;
|
||||
status = MB_ENOERR;
|
||||
} else {
|
||||
@ -294,11 +294,11 @@ mb_err_enum_t mbm_enable(mb_base_t *inst)
|
||||
mb_err_enum_t mbm_disable(mb_base_t *inst)
|
||||
{
|
||||
mb_err_enum_t status = MB_ENOERR;
|
||||
mbm_object_t *mbm_obj = __containerof(inst, mbm_object_t, base);
|
||||
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);;
|
||||
CRITICAL_SECTION(inst->lock)
|
||||
{
|
||||
if (mbm_obj->cur_state == STATE_ENABLED) {
|
||||
mbm_obj->base.transp_obj->frm_stop(mbm_obj->base.transp_obj);
|
||||
MB_OBJ(mbm_obj->base.transp_obj)->frm_stop(mbm_obj->base.transp_obj);
|
||||
mbm_obj->cur_state = STATE_DISABLED;
|
||||
status = MB_ENOERR;
|
||||
} else if (mbm_obj->cur_state == STATE_DISABLED) {
|
||||
@ -312,20 +312,20 @@ mb_err_enum_t mbm_disable(mb_base_t *inst)
|
||||
|
||||
static void mbm_get_pdu_send_buf(mb_base_t *inst, uint8_t **pbuf)
|
||||
{
|
||||
mbm_object_t *mbm_obj = __containerof(inst, mbm_object_t, base);
|
||||
mbm_obj->base.transp_obj->get_tx_frm(mbm_obj->base.transp_obj, pbuf);
|
||||
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);;
|
||||
MB_OBJ(mbm_obj->base.transp_obj)->get_tx_frm(mbm_obj->base.transp_obj, pbuf);
|
||||
}
|
||||
|
||||
__attribute__((unused))
|
||||
static void mbm_get_pdu_recv_buf(mb_base_t *inst, uint8_t **pbuf)
|
||||
{
|
||||
mbm_object_t *mbm_obj = __containerof(inst, mbm_object_t, base);
|
||||
mbm_obj->base.transp_obj->get_rx_frm(mbm_obj->base.transp_obj, pbuf);
|
||||
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);;
|
||||
MB_OBJ(mbm_obj->base.transp_obj)->get_rx_frm(mbm_obj->base.transp_obj, pbuf);
|
||||
}
|
||||
|
||||
static void mbm_set_pdu_send_length(mb_base_t *inst, uint16_t length)
|
||||
{
|
||||
mbm_object_t *mbm_obj = __containerof(inst, mbm_object_t, base);
|
||||
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);;
|
||||
CRITICAL_SECTION(inst->lock)
|
||||
{
|
||||
mbm_obj->pdu_snd_len = length;
|
||||
@ -334,13 +334,13 @@ static void mbm_set_pdu_send_length(mb_base_t *inst, uint16_t length)
|
||||
|
||||
static uint16_t mbm_get_pdu_send_length(mb_base_t *inst)
|
||||
{
|
||||
mbm_object_t *mbm_obj = __containerof(inst, mbm_object_t, base);
|
||||
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);;
|
||||
return mbm_obj->pdu_snd_len;
|
||||
}
|
||||
|
||||
static void mbm_set_dest_addr(mb_base_t *inst, uint8_t dest_addr)
|
||||
{
|
||||
mbm_object_t *mbm_obj = __containerof(inst, mbm_object_t, base);
|
||||
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);;
|
||||
CRITICAL_SECTION(inst->lock)
|
||||
{
|
||||
mbm_obj->master_dst_addr = dest_addr;
|
||||
@ -349,37 +349,37 @@ static void mbm_set_dest_addr(mb_base_t *inst, uint8_t dest_addr)
|
||||
|
||||
static uint8_t mbm_get_dest_addr(mb_base_t *inst)
|
||||
{
|
||||
mbm_object_t *mbm_obj = __containerof(inst, mbm_object_t, base);
|
||||
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);;
|
||||
return mbm_obj->master_dst_addr;
|
||||
}
|
||||
|
||||
void mbm_error_cb_respond_timeout(mb_base_t *inst, uint8_t dest_addr, const uint8_t *pdu_data, uint16_t pdu_length)
|
||||
{
|
||||
mb_port_evt_set_resp_flag(MB_BASE2PORT(inst), EV_MASTER_ERROR_RESPOND_TIMEOUT);
|
||||
ESP_LOG_BUFFER_HEX_LEVEL(__func__, (void *)pdu_data, pdu_length, ESP_LOG_WARN);
|
||||
mb_port_event_set_resp_flag(MB_BASE2PORT(inst), EV_MASTER_ERROR_RESPOND_TIMEOUT);
|
||||
ESP_LOG_BUFFER_HEX_LEVEL(__func__, (void *)pdu_data, pdu_length, ESP_LOG_DEBUG);
|
||||
}
|
||||
|
||||
void mbm_error_cb_receive_data(mb_base_t *inst, uint8_t dest_addr, const uint8_t *pdu_data, uint16_t pdu_length)
|
||||
{
|
||||
mb_port_evt_set_resp_flag(MB_BASE2PORT(inst), EV_MASTER_ERROR_RECEIVE_DATA);
|
||||
mb_port_event_set_resp_flag(MB_BASE2PORT(inst), EV_MASTER_ERROR_RECEIVE_DATA);
|
||||
ESP_LOG_BUFFER_HEX_LEVEL(__func__, (void *)pdu_data, pdu_length, ESP_LOG_DEBUG);
|
||||
}
|
||||
|
||||
void mbm_error_cb_execute_function(mb_base_t *inst, uint8_t dest_address, const uint8_t *pdu_data, uint16_t pdu_length)
|
||||
{
|
||||
mb_port_evt_set_resp_flag(MB_BASE2PORT(inst), EV_MASTER_ERROR_EXECUTE_FUNCTION);
|
||||
mb_port_event_set_resp_flag(MB_BASE2PORT(inst), EV_MASTER_ERROR_EXECUTE_FUNCTION);
|
||||
ESP_LOG_BUFFER_HEX_LEVEL(__func__, (void *)pdu_data, pdu_length, ESP_LOG_DEBUG);
|
||||
}
|
||||
|
||||
void mbm_error_cb_request_success(mb_base_t *inst, uint8_t dest_address, const uint8_t *pdu_data, uint16_t pdu_length)
|
||||
{
|
||||
mb_port_evt_set_resp_flag(MB_BASE2PORT(inst), EV_MASTER_PROCESS_SUCCESS);
|
||||
mb_port_event_set_resp_flag(MB_BASE2PORT(inst), EV_MASTER_PROCESS_SUCCESS);
|
||||
ESP_LOG_BUFFER_HEX_LEVEL(__func__, (void *)pdu_data, pdu_length, ESP_LOG_DEBUG);
|
||||
}
|
||||
|
||||
mb_err_enum_t mbm_poll(mb_base_t *inst)
|
||||
{
|
||||
mbm_object_t *mbm_obj = __containerof(inst, mbm_object_t, base);
|
||||
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);;
|
||||
|
||||
uint16_t length;
|
||||
mb_exception_t exception;
|
||||
@ -394,69 +394,78 @@ mb_err_enum_t mbm_poll(mb_base_t *inst)
|
||||
|
||||
/* Check if there is a event available. If not return control to caller.
|
||||
* Otherwise we will handle the event. */
|
||||
if (mb_port_evt_get(mbm_obj->base.port_obj, &event)) {
|
||||
if (mb_port_event_get(MB_OBJ(mbm_obj->base.port_obj), &event)) {
|
||||
switch (event.event) {
|
||||
case EV_READY:
|
||||
ESP_LOGW(TAG, MB_OBJ_FMT":EV_READY", MB_OBJ_PTR(inst));
|
||||
mb_port_evt_res_release(inst->port_obj);
|
||||
ESP_LOGD(TAG, MB_OBJ_FMT":EV_READY", MB_OBJ_PARENT(inst));
|
||||
mb_port_event_res_release(MB_OBJ(inst->port_obj));
|
||||
break;
|
||||
|
||||
case EV_FRAME_TRANSMIT:
|
||||
mbm_get_pdu_send_buf(inst, &mbm_obj->snd_frame);
|
||||
ESP_LOG_BUFFER_HEX_LEVEL(MB_STR_CAT(inst->descr.parent_name, ":MB_TRANSMIT"), (void *)mbm_obj->snd_frame, mbm_obj->pdu_snd_len, ESP_LOG_WARN);
|
||||
status = inst->transp_obj->frm_send(inst->transp_obj, mbm_obj->master_dst_addr, mbm_obj->snd_frame, mbm_obj->pdu_snd_len);
|
||||
ESP_LOG_BUFFER_HEX_LEVEL(MB_STR_CAT(inst->descr.parent_name, ":MB_TRANSMIT"),
|
||||
(void *)mbm_obj->snd_frame, mbm_obj->pdu_snd_len, ESP_LOG_DEBUG);
|
||||
status = MB_OBJ(inst->transp_obj)->frm_send(inst->transp_obj, mbm_obj->master_dst_addr,
|
||||
mbm_obj->snd_frame, mbm_obj->pdu_snd_len);
|
||||
if (status != MB_ENOERR) {
|
||||
mb_port_evt_set_err_type(inst->port_obj, EV_ERROR_RECEIVE_DATA);
|
||||
(void)mb_port_evt_post(inst->port_obj, EVENT(EV_ERROR_PROCESS));
|
||||
ESP_LOGE(TAG, MB_OBJ_FMT", frame send error. %d", MB_OBJ_PTR(inst), (int)status);
|
||||
mb_port_event_set_err_type(MB_OBJ(inst->port_obj), EV_ERROR_RECEIVE_DATA);
|
||||
(void)mb_port_event_post(MB_OBJ(inst->port_obj), EVENT(EV_ERROR_PROCESS));
|
||||
ESP_LOGE(TAG, MB_OBJ_FMT", frame send error. %d", MB_OBJ_PARENT(inst), (int)status);
|
||||
}
|
||||
// Initialize modbus transaction
|
||||
mbm_obj->curr_trans_id = event.trans_id;
|
||||
break;
|
||||
|
||||
case EV_FRAME_SENT:
|
||||
ESP_LOGD(TAG, MB_OBJ_FMT":EV_MASTER_FRAME_SENT", MB_OBJ_PTR(inst));
|
||||
ESP_LOGD(TAG, MB_OBJ_FMT":EV_FRAME_SENT", MB_OBJ_PARENT(inst));
|
||||
break;
|
||||
|
||||
case EV_FRAME_RECEIVED:
|
||||
ESP_LOGD(TAG, MB_OBJ_FMT":EV_FRAME_RECEIVED", MB_OBJ_PARENT(inst));
|
||||
mbm_obj->pdu_rcv_len = event.length;
|
||||
status = inst->transp_obj->frm_rcv(inst->transp_obj, &mbm_obj->rcv_addr, &mbm_obj->rcv_frame, &mbm_obj->pdu_rcv_len);
|
||||
status = MB_OBJ(inst->transp_obj)->frm_rcv(inst->transp_obj, &mbm_obj->rcv_addr,
|
||||
&mbm_obj->rcv_frame, &mbm_obj->pdu_rcv_len);
|
||||
MB_RETURN_ON_FALSE(mbm_obj->snd_frame, MB_EILLSTATE, TAG, "Send buffer initialization fail.");
|
||||
if (event.trans_id == mbm_obj->curr_trans_id) {
|
||||
// Check if the frame is for us. If not ,send an error process event.
|
||||
if ((status == MB_ENOERR) && ((mbm_obj->rcv_addr == mbm_obj->master_dst_addr)
|
||||
if ((status == MB_ENOERR) && ((mbm_obj->rcv_addr == mbm_obj->master_dst_addr)
|
||||
|| (mbm_obj->rcv_addr == MB_TCP_PSEUDO_ADDRESS))) {
|
||||
if ((mbm_obj->rcv_frame[MB_PDU_FUNC_OFF] & ~MB_FUNC_ERROR) == (mbm_obj->snd_frame[MB_PDU_FUNC_OFF])) {
|
||||
ESP_LOGD(TAG, MB_OBJ_FMT", frame data received successfully, (%d).", MB_OBJ_PTR(inst), (int)status);
|
||||
ESP_LOGD(TAG, MB_OBJ_FMT", frame data received successfully, (%d).", MB_OBJ_PARENT(inst), (int)status);
|
||||
ESP_LOG_BUFFER_HEX_LEVEL(MB_STR_CAT(inst->descr.parent_name, ":MB_RECV"), (void *)mbm_obj->rcv_frame,
|
||||
(uint16_t)mbm_obj->pdu_rcv_len, ESP_LOG_WARN);
|
||||
(void)mb_port_evt_post(inst->port_obj, EVENT(EV_EXECUTE));
|
||||
(uint16_t)mbm_obj->pdu_rcv_len, ESP_LOG_DEBUG);
|
||||
(void)mb_port_event_post(MB_OBJ(inst->port_obj), EVENT(EV_EXECUTE));
|
||||
} else {
|
||||
ESP_LOGE(TAG, MB_OBJ_FMT", drop incorrect frame, receive_func(%u) != send_func(%u)",
|
||||
MB_OBJ_PTR(inst), (mbm_obj->rcv_frame[MB_PDU_FUNC_OFF] & ~MB_FUNC_ERROR),
|
||||
MB_OBJ_PARENT(inst), (mbm_obj->rcv_frame[MB_PDU_FUNC_OFF] & ~MB_FUNC_ERROR),
|
||||
mbm_obj->snd_frame[MB_PDU_FUNC_OFF]);
|
||||
mb_port_evt_set_err_type(inst->port_obj, EV_ERROR_RECEIVE_DATA);
|
||||
(void)mb_port_evt_post(inst->port_obj, EVENT(EV_ERROR_PROCESS));
|
||||
mb_port_event_set_err_type(MB_OBJ(inst->port_obj), EV_ERROR_RECEIVE_DATA);
|
||||
(void)mb_port_event_post(MB_OBJ(inst->port_obj), EVENT(EV_ERROR_PROCESS));
|
||||
}
|
||||
} else {
|
||||
mb_port_evt_set_err_type(inst->port_obj, EV_ERROR_RECEIVE_DATA);
|
||||
(void)mb_port_evt_post(inst->port_obj, EVENT(EV_ERROR_PROCESS));
|
||||
mb_port_event_set_err_type(MB_OBJ(inst->port_obj), EV_ERROR_RECEIVE_DATA);
|
||||
(void)mb_port_event_post(MB_OBJ(inst->port_obj), EVENT(EV_ERROR_PROCESS));
|
||||
ESP_LOGD(TAG, MB_OBJ_FMT", packet data receive failed (addr=%u)(%u).",
|
||||
MB_OBJ_PTR(inst), (unsigned)mbm_obj->rcv_addr, (unsigned)status);
|
||||
MB_OBJ_PARENT(inst), (unsigned)mbm_obj->rcv_addr, (unsigned)status);
|
||||
}
|
||||
} else {
|
||||
// Ignore the `EV_FRAME_RECEIVED` event because the respond timeout occurred
|
||||
// and this is likely respond to previous transaction
|
||||
ESP_LOGE(TAG, MB_OBJ_FMT", drop data received outside of transaction.", MB_OBJ_PTR(inst));
|
||||
mb_port_evt_set_err_type(inst->port_obj, EV_ERROR_RESPOND_TIMEOUT);
|
||||
(void)mb_port_evt_post(inst->port_obj, EVENT(EV_ERROR_PROCESS));
|
||||
ESP_LOGE(TAG, MB_OBJ_FMT", drop data received outside of transaction.", MB_OBJ_PARENT(inst));
|
||||
mb_port_event_set_err_type(MB_OBJ(inst->port_obj), EV_ERROR_RESPOND_TIMEOUT);
|
||||
(void)mb_port_event_post(MB_OBJ(inst->port_obj), EVENT(EV_ERROR_PROCESS));
|
||||
}
|
||||
break;
|
||||
|
||||
case EV_EXECUTE:
|
||||
if (event.trans_id == mbm_obj->curr_trans_id) {
|
||||
MB_RETURN_ON_FALSE(mbm_obj->rcv_frame, MB_EILLSTATE, TAG, MB_OBJ_FMT", receive buffer initialization fail.", MB_OBJ_PTR(inst));
|
||||
ESP_LOGD(TAG, MB_OBJ_FMT":EV_EXECUTE", MB_OBJ_PTR(inst));
|
||||
if (MB_OBJ(inst->transp_obj)->frm_is_bcast(inst->transp_obj)
|
||||
&& ((mbm_obj->cur_mode == MB_RTU) || (mbm_obj->cur_mode == MB_ASCII))) {
|
||||
mbm_obj->rcv_frame = mbm_obj->snd_frame;
|
||||
}
|
||||
MB_RETURN_ON_FALSE(mbm_obj->rcv_frame, MB_EILLSTATE, TAG,
|
||||
MB_OBJ_FMT", receive buffer initialization fail.", MB_OBJ_PARENT(inst));
|
||||
ESP_LOGD(TAG, MB_OBJ_FMT":EV_EXECUTE", MB_OBJ_PARENT(inst));
|
||||
mbm_obj->func_code = mbm_obj->rcv_frame[MB_PDU_FUNC_OFF];
|
||||
exception = MB_EX_ILLEGAL_FUNCTION;
|
||||
/* If receive frame has exception. The receive function code highest bit is 1.*/
|
||||
@ -472,7 +481,7 @@ mb_err_enum_t mbm_poll(mb_base_t *inst)
|
||||
/* If master request is broadcast,
|
||||
* the master need execute function for all slave.
|
||||
*/
|
||||
if (inst->transp_obj->frm_is_bcast(inst->transp_obj)) {
|
||||
if (MB_OBJ(inst->transp_obj)->frm_is_bcast(inst->transp_obj)) {
|
||||
length = mbm_obj->pdu_snd_len;
|
||||
for (int j = 1; j <= MB_MASTER_TOTAL_SLAVE_NUM; j++) {
|
||||
mbm_set_dest_addr(inst, j);
|
||||
@ -487,28 +496,28 @@ mb_err_enum_t mbm_poll(mb_base_t *inst)
|
||||
}
|
||||
/* If master has exception, will send error process event. Otherwise the master is idle.*/
|
||||
if (exception != MB_EX_NONE) {
|
||||
mb_port_evt_set_err_type(inst->port_obj, EV_ERROR_EXECUTE_FUNCTION);
|
||||
(void)mb_port_evt_post(inst->port_obj, EVENT(EV_ERROR_PROCESS));
|
||||
mb_port_event_set_err_type(MB_OBJ(inst->port_obj), EV_ERROR_EXECUTE_FUNCTION);
|
||||
(void)mb_port_event_post(MB_OBJ(inst->port_obj), EVENT(EV_ERROR_PROCESS));
|
||||
} else {
|
||||
error_type = mb_port_evt_get_err_type(inst->port_obj);
|
||||
error_type = mb_port_event_get_err_type(MB_OBJ(inst->port_obj));
|
||||
if (error_type == EV_ERROR_INIT) {
|
||||
ESP_LOGD(TAG, MB_OBJ_FMT", set event EV_ERROR_OK", MB_OBJ_PTR(inst));
|
||||
mb_port_evt_set_err_type(inst->port_obj, EV_ERROR_OK);
|
||||
(void)mb_port_evt_post(inst->port_obj, EVENT(EV_ERROR_PROCESS));
|
||||
ESP_LOGD(TAG, MB_OBJ_FMT", set event EV_ERROR_OK", MB_OBJ_PARENT(inst));
|
||||
mb_port_event_set_err_type(MB_OBJ(inst->port_obj), EV_ERROR_OK);
|
||||
(void)mb_port_event_post(MB_OBJ(inst->port_obj), EVENT(EV_ERROR_PROCESS));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mb_port_evt_set_err_type(inst->port_obj, EV_ERROR_EXECUTE_FUNCTION);
|
||||
(void)mb_port_evt_post(inst->port_obj, EVENT(EV_ERROR_PROCESS));
|
||||
ESP_LOGE(TAG, MB_OBJ_FMT", execution is expired.", MB_OBJ_PTR(inst));
|
||||
mb_port_event_set_err_type(MB_OBJ(inst->port_obj), EV_ERROR_EXECUTE_FUNCTION);
|
||||
(void)mb_port_event_post(MB_OBJ(inst->port_obj), EVENT(EV_ERROR_PROCESS));
|
||||
ESP_LOGE(TAG, MB_OBJ_FMT", execution is expired.", MB_OBJ_PARENT(inst));
|
||||
}
|
||||
break;
|
||||
|
||||
case EV_ERROR_PROCESS:
|
||||
ESP_LOGW(TAG, MB_OBJ_FMT":EV_ERROR_PROCESS", MB_OBJ_PTR(inst));
|
||||
ESP_LOGD(TAG, MB_OBJ_FMT":EV_ERROR_PROCESS", MB_OBJ_PARENT(inst));
|
||||
// stop timer and execute specified error process callback function.
|
||||
mb_port_tmr_disable(inst->port_obj);
|
||||
error_type = mb_port_evt_get_err_type(inst->port_obj);
|
||||
mb_port_timer_disable(MB_OBJ(inst->port_obj));
|
||||
error_type = mb_port_event_get_err_type(MB_OBJ(inst->port_obj));
|
||||
mbm_get_pdu_send_buf(inst, &mbm_obj->snd_frame);
|
||||
switch (error_type)
|
||||
{
|
||||
@ -529,23 +538,23 @@ mb_err_enum_t mbm_poll(mb_base_t *inst)
|
||||
mbm_obj->snd_frame, mbm_obj->pdu_snd_len);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, MB_OBJ_FMT", incorrect error type = %d.", MB_OBJ_PTR(inst), (int)error_type);
|
||||
ESP_LOGE(TAG, MB_OBJ_FMT", incorrect error type = %d.", MB_OBJ_PARENT(inst), (int)error_type);
|
||||
break;
|
||||
}
|
||||
mb_port_evt_set_err_type(inst->port_obj, EV_ERROR_INIT);
|
||||
mb_port_event_set_err_type(MB_OBJ(inst->port_obj), EV_ERROR_INIT);
|
||||
uint64_t time_div_us = mbm_obj->curr_trans_id ? (event.get_ts - mbm_obj->curr_trans_id) : 0;
|
||||
mbm_obj->curr_trans_id = 0;
|
||||
ESP_LOGW(TAG, MB_OBJ_FMT", transaction processing time(us) = %" PRId64, MB_OBJ_PTR(inst), time_div_us);
|
||||
mb_port_evt_res_release(inst->port_obj);
|
||||
ESP_LOGD(TAG, MB_OBJ_FMT", transaction processing time(us) = %" PRId64, MB_OBJ_PARENT(inst), time_div_us);
|
||||
mb_port_event_res_release(MB_OBJ(inst->port_obj));
|
||||
break;
|
||||
|
||||
default:
|
||||
ESP_LOGE(TAG, MB_OBJ_FMT", unexpected event triggered 0x%02x.", MB_OBJ_PTR(inst), (int)event.event);
|
||||
ESP_LOGE(TAG, MB_OBJ_FMT", unexpected event triggered 0x%02x.", MB_OBJ_PARENT(inst), (int)event.event);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Something went wrong and task unblocked but there are no any correct events set
|
||||
ESP_LOGE(TAG, MB_OBJ_FMT", unexpected event triggered 0x%02x.", MB_OBJ_PTR(inst), (int)event.event);
|
||||
ESP_LOGE(TAG, MB_OBJ_FMT", unexpected event triggered 0x%02x.", MB_OBJ_PARENT(inst), (int)event.event);
|
||||
status = MB_EILLSTATE;
|
||||
}
|
||||
return status;
|
||||
|
@ -225,12 +225,12 @@ error:
|
||||
|
||||
mb_err_enum_t mbs_delete(mb_base_t *inst)
|
||||
{
|
||||
mbs_object_t *mbs_obj = __containerof(inst, mbs_object_t, base);
|
||||
mbs_object_t *mbs_obj = MB_GET_OBJ_CTX(inst, mbs_object_t, base);
|
||||
mb_err_enum_t status = MB_ENOERR;
|
||||
if (mbs_obj->cur_state == STATE_DISABLED) {
|
||||
if (mbs_obj->base.transp_obj->frm_delete) {
|
||||
if (MB_OBJ(mbs_obj->base.transp_obj)->frm_delete) {
|
||||
// call destructor of the transport object
|
||||
mbs_obj->base.transp_obj->frm_delete(inst->transp_obj);
|
||||
MB_OBJ(mbs_obj->base.transp_obj)->frm_delete(inst->transp_obj);
|
||||
}
|
||||
// delete the modbus instance
|
||||
free(mbs_obj->base.descr.parent_name);
|
||||
@ -247,12 +247,12 @@ mb_err_enum_t mbs_delete(mb_base_t *inst)
|
||||
|
||||
mb_err_enum_t mbs_enable(mb_base_t *inst)
|
||||
{
|
||||
mbs_object_t *mbs_obj = __containerof(inst, mbs_object_t, base);
|
||||
mbs_object_t *mbs_obj = MB_GET_OBJ_CTX(inst, mbs_object_t, base);
|
||||
mb_err_enum_t status = MB_ENOERR;
|
||||
CRITICAL_SECTION(inst->lock) {
|
||||
if (mbs_obj->cur_state == STATE_DISABLED) {
|
||||
/* Activate the protocol stack. */
|
||||
mbs_obj->base.transp_obj->frm_start(mbs_obj->base.transp_obj);
|
||||
MB_OBJ(mbs_obj->base.transp_obj)->frm_start(mbs_obj->base.transp_obj);
|
||||
mbs_obj->cur_state = STATE_ENABLED;
|
||||
status = MB_ENOERR;
|
||||
} else {
|
||||
@ -269,10 +269,10 @@ mb_err_enum_t mbs_enable(mb_base_t *inst)
|
||||
mb_err_enum_t mbs_disable(mb_base_t *inst)
|
||||
{
|
||||
mb_err_enum_t status = MB_ENOERR;
|
||||
mbs_object_t *mbs_obj = __containerof(inst, mbs_object_t, base);
|
||||
mbs_object_t *mbs_obj = MB_GET_OBJ_CTX(inst, mbs_object_t, base);;
|
||||
CRITICAL_SECTION(inst->lock) {
|
||||
if (mbs_obj->cur_state == STATE_ENABLED) {
|
||||
mbs_obj->base.transp_obj->frm_stop(mbs_obj->base.transp_obj);
|
||||
MB_OBJ(mbs_obj->base.transp_obj)->frm_stop(mbs_obj->base.transp_obj);
|
||||
mbs_obj->cur_state = STATE_DISABLED;
|
||||
status = MB_ENOERR;
|
||||
} else if (mbs_obj->cur_state == STATE_DISABLED) {
|
||||
@ -286,7 +286,7 @@ mb_err_enum_t mbs_disable(mb_base_t *inst)
|
||||
|
||||
mb_err_enum_t mbs_poll(mb_base_t *inst)
|
||||
{
|
||||
mbs_object_t *mbs_obj = __containerof(inst, mbs_object_t, base);
|
||||
mbs_object_t *mbs_obj = MB_GET_OBJ_CTX(inst, mbs_object_t, base);;
|
||||
|
||||
mb_exception_t exception;
|
||||
mb_err_enum_t status = MB_ENOERR;
|
||||
@ -298,45 +298,44 @@ mb_err_enum_t mbs_poll(mb_base_t *inst)
|
||||
}
|
||||
|
||||
/* Check if there is a event available. If not, return control to caller. Otherwise we will handle the event. */
|
||||
if (mb_port_evt_get(mbs_obj->base.port_obj, &event)) {
|
||||
if (mb_port_event_get(MB_OBJ(mbs_obj->base.port_obj), &event)) {
|
||||
switch(event.event) {
|
||||
case EV_READY:
|
||||
ESP_LOGW(TAG, MB_OBJ_FMT":EV_READY", MB_OBJ_PTR(inst));
|
||||
mb_port_evt_res_release(inst->port_obj);
|
||||
ESP_LOGD(TAG, MB_OBJ_FMT":EV_READY", MB_OBJ_PARENT(inst));
|
||||
mb_port_event_res_release(MB_OBJ(inst->port_obj));
|
||||
break;
|
||||
|
||||
case EV_FRAME_RECEIVED:
|
||||
ESP_LOGD(TAG, MB_OBJ_FMT":EV_FRAME_RECEIVED", MB_OBJ_PTR(inst));
|
||||
ESP_LOGD(TAG, MB_OBJ_FMT":EV_FRAME_RECEIVED", MB_OBJ_PARENT(inst));
|
||||
mbs_obj->length = event.length;
|
||||
status = inst->transp_obj->frm_rcv(inst->transp_obj, &mbs_obj->rcv_addr, &mbs_obj->frame, &mbs_obj->length);
|
||||
status = MB_OBJ(inst->transp_obj)->frm_rcv(inst->transp_obj, &mbs_obj->rcv_addr, &mbs_obj->frame, &mbs_obj->length);
|
||||
// Check if the frame is for us. If not ,send an error process event.
|
||||
if (status == MB_ENOERR) {
|
||||
// Check if the frame is for us. If not ignore the frame.
|
||||
if((mbs_obj->rcv_addr == mbs_obj->mb_address) || (mbs_obj->rcv_addr == MB_ADDRESS_BROADCAST)
|
||||
|| (mbs_obj->rcv_addr == MB_TCP_PSEUDO_ADDRESS)) {
|
||||
mbs_obj->curr_trans_id = event.get_ts;
|
||||
(void)mb_port_evt_post(inst->port_obj, EVENT(EV_EXECUTE | EV_TRANS_START));
|
||||
(void)mb_port_event_post(MB_OBJ(inst->port_obj), EVENT(EV_EXECUTE | EV_TRANS_START));
|
||||
ESP_LOG_BUFFER_HEX_LEVEL(MB_STR_CAT(inst->descr.parent_name, ":MB_RECV"), &mbs_obj->frame[MB_PDU_FUNC_OFF],
|
||||
(uint16_t)mbs_obj->length, ESP_LOG_WARN);
|
||||
(uint16_t)mbs_obj->length, ESP_LOG_DEBUG);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case EV_EXECUTE:
|
||||
MB_RETURN_ON_FALSE(mbs_obj->frame, MB_EILLSTATE, TAG, "receive buffer fail.");
|
||||
ESP_LOGD(TAG, MB_OBJ_FMT":EV_EXECUTE", MB_OBJ_PTR(inst));
|
||||
ESP_LOGD(TAG, MB_OBJ_FMT":EV_EXECUTE", MB_OBJ_PARENT(inst));
|
||||
mbs_obj->func_code = mbs_obj->frame[MB_PDU_FUNC_OFF];
|
||||
exception = MB_EX_ILLEGAL_FUNCTION;
|
||||
// If receive frame has exception. The receive function code highest bit is 1.
|
||||
for (int i = 0; (i < MB_FUNC_HANDLERS_MAX); i++) {
|
||||
// No more function handlers registered. Abort.
|
||||
if (mbs_obj->func_handlers[i].func_code == 0) {
|
||||
ESP_LOGD(TAG, MB_OBJ_FMT": function (0x%x), handler is not found.", MB_OBJ_PARENT(inst), (int)mbs_obj->func_code);
|
||||
break;
|
||||
}
|
||||
if ((mbs_obj->func_handlers[i].func_code) == mbs_obj->func_code) {
|
||||
if (mbs_obj->func_code == 0x04) {
|
||||
ESP_LOGW(TAG, "Read input registers");
|
||||
}
|
||||
ESP_LOGD(TAG, MB_OBJ_FMT": function (0x%x), start handler.", MB_OBJ_PARENT(inst), (int)mbs_obj->func_code);
|
||||
exception = mbs_obj->func_handlers[i].handler(inst, mbs_obj->frame, &mbs_obj->length);
|
||||
break;
|
||||
}
|
||||
@ -350,34 +349,34 @@ mb_err_enum_t mbs_poll(mb_base_t *inst)
|
||||
mbs_obj->frame[mbs_obj->length++] = exception;
|
||||
}
|
||||
if ((mbs_obj->cur_mode == MB_ASCII) && MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS) {
|
||||
mb_port_tmr_delay(inst->port_obj, MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS);
|
||||
mb_port_timer_delay(MB_OBJ(inst->port_obj), MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS);
|
||||
}
|
||||
ESP_LOG_BUFFER_HEX_LEVEL(MB_STR_CAT(inst->descr.parent_name, ":MB_SEND"), (void *)mbs_obj->frame, mbs_obj->length, ESP_LOG_WARN);
|
||||
status = inst->transp_obj->frm_send(inst->transp_obj, mbs_obj->rcv_addr, mbs_obj->frame, mbs_obj->length);
|
||||
ESP_LOG_BUFFER_HEX_LEVEL(MB_STR_CAT(inst->descr.parent_name, ":MB_SEND"), (void *)mbs_obj->frame, mbs_obj->length, ESP_LOG_DEBUG);
|
||||
status = MB_OBJ(inst->transp_obj)->frm_send(inst->transp_obj, mbs_obj->rcv_addr, mbs_obj->frame, mbs_obj->length);
|
||||
if (status != MB_ENOERR) {
|
||||
ESP_LOGE(TAG, MB_OBJ_FMT":frame send error. %d", MB_OBJ_PTR(inst), (int)status);
|
||||
ESP_LOGE(TAG, MB_OBJ_FMT":frame send error. %d", MB_OBJ_PARENT(inst), (int)status);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case EV_FRAME_TRANSMIT:
|
||||
ESP_LOGD(TAG, MB_OBJ_FMT":EV_FRAME_TRANSMIT", MB_OBJ_PTR(inst));
|
||||
ESP_LOGD(TAG, MB_OBJ_FMT":EV_FRAME_TRANSMIT", MB_OBJ_PARENT(inst));
|
||||
break;
|
||||
|
||||
case EV_FRAME_SENT:
|
||||
ESP_LOGD(TAG, MB_OBJ_FMT":EV_MASTER_FRAME_SENT", MB_OBJ_PTR(inst));
|
||||
ESP_LOGD(TAG, MB_OBJ_FMT":EV_MASTER_FRAME_SENT", MB_OBJ_PARENT(inst));
|
||||
uint64_t time_div_us = mbs_obj->curr_trans_id ? (event.get_ts - mbs_obj->curr_trans_id) : 0;
|
||||
mbs_obj->curr_trans_id = 0;
|
||||
ESP_LOGW(TAG, MB_OBJ_FMT", transaction processing time(us) = %" PRId64, MB_OBJ_PTR(inst), time_div_us);
|
||||
ESP_LOGD(TAG, MB_OBJ_FMT", transaction processing time(us) = %" PRId64, MB_OBJ_PARENT(inst), time_div_us);
|
||||
break;
|
||||
|
||||
default:
|
||||
ESP_LOGD(TAG, MB_OBJ_FMT": Unexpected event triggered 0x%02x.", MB_OBJ_PTR(inst), (int)event.event);
|
||||
ESP_LOGD(TAG, MB_OBJ_FMT": Unexpected event triggered 0x%02x.", MB_OBJ_PARENT(inst), (int)event.event);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Something went wrong and task unblocked but there are no any correct events set
|
||||
ESP_LOGD(TAG, MB_OBJ_FMT": Unexpected event triggered 0x%02x, timeout?", MB_OBJ_PTR(inst), (int)event.event);
|
||||
ESP_LOGD(TAG, MB_OBJ_FMT": Unexpected event triggered 0x%02x, timeout?", MB_OBJ_PARENT(inst), (int)event.event);
|
||||
status = MB_EILLSTATE;
|
||||
}
|
||||
return status;
|
||||
|
278
modbus/mb_ports/common/mb_transaction.c
Normal file
278
modbus/mb_ports/common/mb_transaction.c
Normal file
@ -0,0 +1,278 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdatomic.h>
|
||||
#include "sys/queue.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_log.h"
|
||||
#include "mb_transaction.h"
|
||||
|
||||
static const char *TAG = "mb_transaction";
|
||||
|
||||
/**
|
||||
* @brief transaction list item
|
||||
*/
|
||||
typedef struct transaction_item {
|
||||
uint8_t *buffer;
|
||||
uint16_t len;
|
||||
int node_id;
|
||||
int msg_id;
|
||||
void *pnode;
|
||||
transaction_tick_t tick;
|
||||
_Atomic pending_state_t state;
|
||||
STAILQ_ENTRY(transaction_item) next;
|
||||
} transaction_item_t;
|
||||
|
||||
STAILQ_HEAD(transaction_list_t, transaction_item);
|
||||
|
||||
struct transaction_t {
|
||||
_lock_t lock;
|
||||
uint64_t size;
|
||||
struct transaction_list_t *list;
|
||||
};
|
||||
|
||||
transaction_handle_t transaction_init(void)
|
||||
{
|
||||
transaction_handle_t transaction = calloc(1, sizeof(struct transaction_t));
|
||||
ESP_MEM_CHECK(TAG, transaction, return NULL);
|
||||
transaction->list = calloc(1, sizeof(struct transaction_list_t));
|
||||
ESP_MEM_CHECK(TAG, transaction->list, {free(transaction); return NULL;});
|
||||
transaction->size = 0;
|
||||
CRITICAL_SECTION_INIT(transaction->lock);
|
||||
STAILQ_INIT(transaction->list);
|
||||
return transaction;
|
||||
}
|
||||
|
||||
transaction_item_handle_t transaction_enqueue(transaction_handle_t transaction, transaction_message_handle_t message, transaction_tick_t tick)
|
||||
{
|
||||
transaction_item_handle_t item = calloc(1, sizeof(transaction_item_t));
|
||||
ESP_MEM_CHECK(TAG, item, {
|
||||
return NULL;
|
||||
});
|
||||
CRITICAL_SECTION_LOCK(transaction->lock);
|
||||
item->tick = tick;
|
||||
item->node_id = message->node_id;
|
||||
item->pnode = message->pnode;
|
||||
item->msg_id = message->msg_id;
|
||||
item->len = message->len;
|
||||
item->state = QUEUED;
|
||||
if (!message->buffer) {
|
||||
item->buffer = heap_caps_malloc(message->len, TRANSACTION_MEMORY);
|
||||
memcpy(item->buffer, message->buffer, message->len);
|
||||
} else {
|
||||
item->buffer = message->buffer;
|
||||
}
|
||||
ESP_MEM_CHECK(TAG, item->buffer, {
|
||||
free(item);
|
||||
CRITICAL_SECTION_UNLOCK(transaction->lock);
|
||||
return NULL;
|
||||
});
|
||||
STAILQ_INSERT_TAIL(transaction->list, item, next);
|
||||
transaction->size += item->len;
|
||||
CRITICAL_SECTION_UNLOCK(transaction->lock);
|
||||
ESP_LOGD(TAG, "ENQUEUE msgid=%x, len=%d, size=%"PRIu64, message->msg_id, message->len, transaction_get_size(transaction));
|
||||
return item;
|
||||
}
|
||||
|
||||
transaction_item_handle_t transaction_get(transaction_handle_t transaction, int msg_id)
|
||||
{
|
||||
transaction_item_handle_t item;
|
||||
CRITICAL_SECTION_LOCK(transaction->lock);
|
||||
STAILQ_FOREACH(item, transaction->list, next) {
|
||||
if (item->msg_id == msg_id) {
|
||||
CRITICAL_SECTION_UNLOCK(transaction->lock);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
CRITICAL_SECTION_UNLOCK(transaction->lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
transaction_item_handle_t transaction_get_first(transaction_handle_t transaction)
|
||||
{
|
||||
transaction_item_handle_t item;
|
||||
if (STAILQ_EMPTY(transaction->list)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
item = STAILQ_FIRST(transaction->list);
|
||||
if (item)
|
||||
{
|
||||
return item;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
transaction_item_handle_t transaction_dequeue(transaction_handle_t transaction, pending_state_t state, transaction_tick_t *tick)
|
||||
{
|
||||
transaction_item_handle_t item;
|
||||
CRITICAL_SECTION_LOCK(transaction->lock);
|
||||
STAILQ_FOREACH(item, transaction->list, next) {
|
||||
if (atomic_load(&(item->state)) == state) {
|
||||
if (tick) {
|
||||
*tick = item->tick;
|
||||
}
|
||||
CRITICAL_SECTION_UNLOCK(transaction->lock);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
CRITICAL_SECTION_UNLOCK(transaction->lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_err_t transaction_delete_item(transaction_handle_t transaction, transaction_item_handle_t item_to_delete)
|
||||
{
|
||||
transaction_item_handle_t item;
|
||||
CRITICAL_SECTION_LOCK(transaction->lock);
|
||||
STAILQ_FOREACH(item, transaction->list, next) {
|
||||
if (item == item_to_delete) {
|
||||
STAILQ_REMOVE(transaction->list, item, transaction_item, next);
|
||||
transaction->size -= item->len;
|
||||
free(item->buffer);
|
||||
free(item);
|
||||
CRITICAL_SECTION_UNLOCK(transaction->lock);
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
CRITICAL_SECTION_UNLOCK(transaction->lock);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
uint8_t *transaction_item_get_data(transaction_item_handle_t item, size_t *len, uint16_t *msg_id, int *node_id)
|
||||
{
|
||||
if (item) {
|
||||
if (len) {
|
||||
*len = item->len;
|
||||
}
|
||||
if (msg_id) {
|
||||
*msg_id = item->msg_id;
|
||||
}
|
||||
if (node_id) {
|
||||
*node_id = item->node_id;
|
||||
}
|
||||
return (uint8_t *)item->buffer;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_err_t transaction_delete(transaction_handle_t transaction, int msg_id)
|
||||
{
|
||||
transaction_item_handle_t item, tmp;
|
||||
CRITICAL_SECTION_LOCK(transaction->lock);
|
||||
STAILQ_FOREACH_SAFE(item, transaction->list, next, tmp) {
|
||||
if (item->msg_id == msg_id) {
|
||||
STAILQ_REMOVE(transaction->list, item, transaction_item, next);
|
||||
transaction->size -= item->len;
|
||||
free(item->buffer);
|
||||
free(item);
|
||||
CRITICAL_SECTION_UNLOCK(transaction->lock);
|
||||
ESP_LOGD(TAG, "DELETED msgid=%x, remain size=%"PRIu64, msg_id, transaction_get_size(transaction));
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
CRITICAL_SECTION_UNLOCK(transaction->lock);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t transaction_set_state(transaction_handle_t transaction, int msg_id, pending_state_t state)
|
||||
{
|
||||
transaction_item_handle_t item = transaction_get(transaction, msg_id);
|
||||
if (item) {
|
||||
atomic_store(&(item->state), state);
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
pending_state_t transaction_item_get_state(transaction_item_handle_t item)
|
||||
{
|
||||
if (item) {
|
||||
return atomic_load(&(item->state));
|
||||
}
|
||||
return INIT;
|
||||
}
|
||||
|
||||
esp_err_t transaction_item_set_state(transaction_item_handle_t item, pending_state_t state)
|
||||
{
|
||||
if (item) {
|
||||
atomic_store(&(item->state), state);
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t transaction_set_tick(transaction_handle_t transaction, int msg_id, transaction_tick_t tick)
|
||||
{
|
||||
transaction_item_handle_t item = transaction_get(transaction, msg_id);
|
||||
if (item) {
|
||||
item->tick = tick;
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
int transaction_delete_single_expired(transaction_handle_t transaction, transaction_tick_t current_tick, transaction_tick_t timeout)
|
||||
{
|
||||
int msg_id = -1;
|
||||
transaction_item_handle_t item;
|
||||
CRITICAL_SECTION_LOCK(transaction->lock);
|
||||
STAILQ_FOREACH(item, transaction->list, next) {
|
||||
if (current_tick - item->tick > timeout) {
|
||||
STAILQ_REMOVE(transaction->list, item, transaction_item, next);
|
||||
free(item->buffer);
|
||||
transaction->size -= item->len;
|
||||
msg_id = item->msg_id;
|
||||
free(item);
|
||||
CRITICAL_SECTION_UNLOCK(transaction->lock);
|
||||
return msg_id;
|
||||
}
|
||||
|
||||
}
|
||||
CRITICAL_SECTION_UNLOCK(transaction->lock);
|
||||
return msg_id;
|
||||
}
|
||||
|
||||
int transaction_delete_expired(transaction_handle_t transaction, transaction_tick_t current_tick, transaction_tick_t timeout)
|
||||
{
|
||||
int deleted_items = 0;
|
||||
transaction_item_handle_t item, tmp;
|
||||
CRITICAL_SECTION_LOCK(transaction->lock);
|
||||
STAILQ_FOREACH_SAFE(item, transaction->list, next, tmp) {
|
||||
if (current_tick - item->tick > timeout) {
|
||||
STAILQ_REMOVE(transaction->list, item, transaction_item, next);
|
||||
free(item->buffer);
|
||||
transaction->size -= item->len;
|
||||
free(item);
|
||||
deleted_items ++;
|
||||
}
|
||||
}
|
||||
CRITICAL_SECTION_UNLOCK(transaction->lock);
|
||||
return deleted_items;
|
||||
}
|
||||
|
||||
uint64_t transaction_get_size(transaction_handle_t transaction)
|
||||
{
|
||||
return transaction->size;
|
||||
}
|
||||
|
||||
void transaction_delete_all_items(transaction_handle_t transaction)
|
||||
{
|
||||
transaction_item_handle_t item, tmp;
|
||||
CRITICAL_SECTION_LOCK(transaction->lock);
|
||||
STAILQ_FOREACH_SAFE(item, transaction->list, next, tmp) {
|
||||
STAILQ_REMOVE(transaction->list, item, transaction_item, next);
|
||||
transaction->size -= item->len;
|
||||
free(item->buffer);
|
||||
free(item);
|
||||
}
|
||||
CRITICAL_SECTION_UNLOCK(transaction->lock);
|
||||
}
|
||||
|
||||
void transaction_destroy(transaction_handle_t transaction)
|
||||
{
|
||||
transaction_delete_all_items(transaction);
|
||||
CRITICAL_SECTION_CLOSE(transaction->lock);
|
||||
free(transaction->list);
|
||||
free(transaction);
|
||||
}
|
||||
|
76
modbus/mb_ports/common/mb_transaction.h
Normal file
76
modbus/mb_ports/common/mb_transaction.h
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "port_common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define TRANSACTION_MEMORY MALLOC_CAP_DEFAULT
|
||||
|
||||
#define ESP_MEM_CHECK(TAG, a, action) if (!(a)) { \
|
||||
ESP_LOGE(TAG,"%s(%d): %s", __FUNCTION__, __LINE__, "Memory exhausted"); \
|
||||
action; \
|
||||
}
|
||||
|
||||
struct transaction_item;
|
||||
|
||||
typedef struct transaction_t *transaction_handle_t;
|
||||
typedef struct transaction_item *transaction_item_handle_t;
|
||||
|
||||
typedef struct transaction_message {
|
||||
uint8_t *buffer;
|
||||
uint16_t len;
|
||||
int msg_id;
|
||||
int node_id;
|
||||
void *pnode;
|
||||
} transaction_message_t;
|
||||
|
||||
typedef struct transaction_message *transaction_message_handle_t;
|
||||
typedef long long transaction_tick_t;
|
||||
|
||||
typedef enum pending_state {
|
||||
INIT,
|
||||
QUEUED,
|
||||
ACKNOWLEDGED,
|
||||
CONFIRMED,
|
||||
REPLIED,
|
||||
RECEIVED,
|
||||
TRANSMITTED,
|
||||
EXPIRED
|
||||
} pending_state_t;
|
||||
|
||||
transaction_handle_t transaction_init(void);
|
||||
transaction_item_handle_t transaction_enqueue(transaction_handle_t transaction, transaction_message_handle_t message, transaction_tick_t tick);
|
||||
transaction_item_handle_t transaction_dequeue(transaction_handle_t transaction, pending_state_t pending, transaction_tick_t *tick);
|
||||
transaction_item_handle_t transaction_get(transaction_handle_t transaction, int msg_id);
|
||||
transaction_item_handle_t transaction_get_first(transaction_handle_t transaction);
|
||||
uint8_t *transaction_item_get_data(transaction_item_handle_t item, size_t *len, uint16_t *msg_id, int *node_id);
|
||||
esp_err_t transaction_delete(transaction_handle_t transaction, int msg_id);
|
||||
esp_err_t transaction_delete_item(transaction_handle_t transaction, transaction_item_handle_t item);
|
||||
int transaction_delete_expired(transaction_handle_t transaction, transaction_tick_t current_tick, transaction_tick_t timeout);
|
||||
|
||||
/**
|
||||
* @brief Deletes single expired message returning it's message id
|
||||
*
|
||||
* @return msg id of the deleted message, -1 if no expired message in the transaction
|
||||
*/
|
||||
int transaction_delete_single_expired(transaction_handle_t transaction, transaction_tick_t current_tick, transaction_tick_t timeout);
|
||||
esp_err_t transaction_set_state(transaction_handle_t transaction, int msg_id, pending_state_t pending);
|
||||
pending_state_t transaction_item_get_state(transaction_item_handle_t item);
|
||||
esp_err_t transaction_item_set_state(transaction_item_handle_t item, pending_state_t state);
|
||||
esp_err_t transaction_set_tick(transaction_handle_t transaction, int msg_id, transaction_tick_t tick);
|
||||
uint64_t transaction_get_size(transaction_handle_t transaction);
|
||||
void transaction_destroy(transaction_handle_t transaction);
|
||||
void transaction_delete_all_items(transaction_handle_t transaction);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -65,6 +65,29 @@ void unlock_obj(_lock_t *plock);
|
||||
spinlock_initialize(&lock); \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define CRITICAL_STORE(LOCK, PTR, VAL) \
|
||||
__extension__ \
|
||||
({ \
|
||||
__auto_type __atomic_ptr = (PTR); \
|
||||
__typeof__ ((void)0, *__atomic_ptr) __atomic_tmp = (VAL); \
|
||||
_lock_acquire((_lock_t *)&LOCK); \
|
||||
*__atomic_ptr = __atomic_tmp; \
|
||||
_lock_release((_lock_t *)&LOCK); \
|
||||
(__atomic_tmp); \
|
||||
})
|
||||
|
||||
#define CRITICAL_LOAD(LOCK, PTR) \
|
||||
__extension__ \
|
||||
({ \
|
||||
__auto_type __atomic_ptr = (PTR); \
|
||||
__typeof__ ((void)0, *__atomic_ptr) __atomic_tmp; \
|
||||
_lock_acquire((_lock_t *)&LOCK); \
|
||||
__atomic_tmp = (*__atomic_ptr); \
|
||||
_lock_release((_lock_t *)&LOCK); \
|
||||
(__atomic_tmp); \
|
||||
})
|
||||
|
||||
#define SPIN_LOCK_ENTER(lock) \
|
||||
do \
|
||||
{ \
|
||||
@ -160,29 +183,30 @@ struct mb_port_base_t
|
||||
};
|
||||
|
||||
// Port event functions
|
||||
mb_err_enum_t mb_port_evt_create(mb_port_base_t *port_obj);
|
||||
bool mb_port_evt_post(mb_port_base_t *inst, mb_event_t event);
|
||||
bool mb_port_evt_get(mb_port_base_t *inst, mb_event_t *event);
|
||||
bool mb_port_evt_res_take(mb_port_base_t *inst, uint32_t timeout);
|
||||
void mb_port_evt_res_release(mb_port_base_t *inst);
|
||||
void mb_port_evt_set_resp_flag(mb_port_base_t *inst, mb_event_enum_t event_mask);
|
||||
void mb_port_evt_set_err_type(mb_port_base_t *inst, mb_err_event_t event);
|
||||
mb_err_event_t mb_port_evt_get_err_type(mb_port_base_t *inst);
|
||||
void mb_port_evt_delete(mb_port_base_t *inst);
|
||||
mb_err_enum_t mb_port_evt_wait_req_finish(mb_port_base_t *inst);
|
||||
mb_err_enum_t mb_port_event_create(mb_port_base_t *port_obj);
|
||||
bool mb_port_event_post(mb_port_base_t *inst, mb_event_t event);
|
||||
bool mb_port_event_get(mb_port_base_t *inst, mb_event_t *event);
|
||||
bool mb_port_event_res_take(mb_port_base_t *inst, uint32_t timeout);
|
||||
void mb_port_event_res_release(mb_port_base_t *inst);
|
||||
void mb_port_event_set_resp_flag(mb_port_base_t *inst, mb_event_enum_t event_mask);
|
||||
void mb_port_event_set_err_type(mb_port_base_t *inst, mb_err_event_t event);
|
||||
mb_err_event_t mb_port_event_get_err_type(mb_port_base_t *inst);
|
||||
void mb_port_event_delete(mb_port_base_t *inst);
|
||||
mb_err_enum_t mb_port_event_wait_req_finish(mb_port_base_t *inst);
|
||||
uint64_t mb_port_get_trans_id(mb_port_base_t *inst);
|
||||
|
||||
// Port timer functions
|
||||
mb_err_enum_t mb_port_tmr_create(mb_port_base_t *inst, uint16_t t35_timer_ticks);
|
||||
void mb_port_tmr_disable(mb_port_base_t *inst);
|
||||
void mb_port_tmr_enable(mb_port_base_t *inst);
|
||||
void mb_port_tmr_respond_timeout_enable(mb_port_base_t *inst);
|
||||
void mb_port_tmr_convert_delay_enable(mb_port_base_t *inst);
|
||||
void mb_port_set_cur_tmr_mode(mb_port_base_t *inst, mb_tmr_mode_enum_t tmr_mode);
|
||||
mb_tmr_mode_enum_t mb_port_get_cur_tmr_mode(mb_port_base_t *inst);
|
||||
void mb_port_tmr_set_response_time(mb_port_base_t *inst, uint32_t resp_time_ms);
|
||||
uint32_t mb_port_tmr_get_response_time_ms(mb_port_base_t *inst);
|
||||
void mb_port_tmr_delay(mb_port_base_t *inst, uint16_t timeout_ms);
|
||||
void mb_port_tmr_delete(mb_port_base_t *inst);
|
||||
mb_err_enum_t mb_port_timer_create(mb_port_base_t *inst, uint16_t t35_timer_ticks);
|
||||
void mb_port_timer_disable(mb_port_base_t *inst);
|
||||
void mb_port_timer_enable(mb_port_base_t *inst);
|
||||
void mb_port_timer_respond_timeout_enable(mb_port_base_t *inst);
|
||||
void mb_port_timer_convert_delay_enable(mb_port_base_t *inst);
|
||||
void mb_port_set_cur_timer_mode(mb_port_base_t *inst, mb_timer_mode_enum_t tmr_mode);
|
||||
mb_timer_mode_enum_t mb_port_get_cur_timer_mode(mb_port_base_t *inst);
|
||||
void mb_port_timer_set_response_time(mb_port_base_t *inst, uint32_t resp_time_ms);
|
||||
uint32_t mb_port_timer_get_response_time_ms(mb_port_base_t *inst);
|
||||
void mb_port_timer_delay(mb_port_base_t *inst, uint16_t timeout_ms);
|
||||
void mb_port_timer_delete(mb_port_base_t *inst);
|
||||
|
||||
// Common functions to track instance descriptors
|
||||
void mb_port_set_inst_counter(uint32_t inst_counter);
|
||||
|
@ -18,14 +18,14 @@ static const char *TAG = "mb_port.event";
|
||||
|
||||
struct mb_port_event_t
|
||||
{
|
||||
volatile mb_err_event_t curr_err_type;
|
||||
_Atomic mb_err_event_t curr_err_type;
|
||||
SemaphoreHandle_t resource_hdl;
|
||||
EventGroupHandle_t event_group_hdl;
|
||||
QueueHandle_t event_hdl;
|
||||
uint64_t curr_trans_id;
|
||||
_Atomic uint64_t curr_trans_id;
|
||||
};
|
||||
|
||||
mb_err_enum_t mb_port_evt_create(mb_port_base_t *inst)
|
||||
mb_err_enum_t mb_port_event_create(mb_port_base_t *inst)
|
||||
{
|
||||
mb_port_event_t *pevent = NULL;
|
||||
mb_err_enum_t ret = MB_EILLSTATE;
|
||||
@ -42,7 +42,7 @@ mb_err_enum_t mb_port_evt_create(mb_port_base_t *inst)
|
||||
MB_GOTO_ON_FALSE((pevent->event_hdl), MB_EILLSTATE, error, TAG, "%s, event queue create error.", inst->descr.parent_name);
|
||||
vQueueAddToRegistry(pevent->event_hdl, TAG);
|
||||
inst->event_obj = pevent;
|
||||
pevent->curr_err_type = EV_ERROR_INIT;
|
||||
atomic_init(&pevent->curr_err_type, EV_ERROR_INIT);
|
||||
ESP_LOGD(TAG, "initialized object @%p", pevent);
|
||||
return MB_ENOERR;
|
||||
|
||||
@ -64,12 +64,12 @@ error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline void mb_port_evt_set_err_type(mb_port_base_t *inst, mb_err_event_t event)
|
||||
inline void mb_port_event_set_err_type(mb_port_base_t *inst, mb_err_event_t event)
|
||||
{
|
||||
atomic_store(&(inst->event_obj->curr_err_type), event);
|
||||
}
|
||||
|
||||
inline mb_err_event_t mb_port_evt_get_err_type(mb_port_base_t *inst)
|
||||
inline mb_err_event_t mb_port_event_get_err_type(mb_port_base_t *inst)
|
||||
{
|
||||
return atomic_load(&inst->event_obj->curr_err_type);
|
||||
}
|
||||
@ -79,7 +79,7 @@ uint64_t mb_port_get_trans_id(mb_port_base_t *inst)
|
||||
return atomic_load(&(inst->event_obj->curr_trans_id));
|
||||
}
|
||||
|
||||
bool mb_port_evt_post(mb_port_base_t *inst, mb_event_t event)
|
||||
bool mb_port_event_post(mb_port_base_t *inst, mb_event_t event)
|
||||
{
|
||||
if (!inst || !inst->event_obj || !inst->event_obj->event_hdl) {
|
||||
ESP_LOGE(TAG, "Wrong event handle %d %p %s.", (int)(event.event), inst, inst->descr.parent_name);
|
||||
@ -116,6 +116,7 @@ bool mb_port_evt_post(mb_port_base_t *inst, mb_event_t event)
|
||||
} else {
|
||||
result = xQueueSend(inst->event_obj->event_hdl, (const void*)&temp_event, MB_EVENT_QUEUE_TIMEOUT_MAX);
|
||||
if (result != pdTRUE) {
|
||||
xQueueReset(inst->event_obj->event_hdl);
|
||||
ESP_LOGE(TAG, "%s, post message failure.", inst->descr.parent_name);
|
||||
return false;
|
||||
}
|
||||
@ -123,7 +124,7 @@ bool mb_port_evt_post(mb_port_base_t *inst, mb_event_t event)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mb_port_evt_get(mb_port_base_t *inst, mb_event_t *pevent)
|
||||
bool mb_port_event_get(mb_port_base_t *inst, mb_event_t *pevent)
|
||||
{
|
||||
assert(inst->event_obj->event_hdl);
|
||||
bool event_happened = false;
|
||||
@ -138,15 +139,15 @@ bool mb_port_evt_get(mb_port_base_t *inst, mb_event_t *pevent)
|
||||
return event_happened;
|
||||
}
|
||||
|
||||
bool mb_port_evt_res_take(mb_port_base_t *inst, uint32_t timeout)
|
||||
bool mb_port_event_res_take(mb_port_base_t *inst, uint32_t timeout)
|
||||
{
|
||||
BaseType_t status = pdTRUE;
|
||||
status = xSemaphoreTake(inst->event_obj->resource_hdl, timeout);
|
||||
ESP_LOGW(TAG, "%s, mb take resource, (%" PRIu32 " ticks).", inst->descr.parent_name, timeout);
|
||||
ESP_LOGD(TAG, "%s, mb take resource, (%" PRIu32 " ticks).", inst->descr.parent_name, timeout);
|
||||
return status;
|
||||
}
|
||||
|
||||
void mb_port_evt_res_release(mb_port_base_t *inst)
|
||||
void mb_port_event_res_release(mb_port_base_t *inst)
|
||||
{
|
||||
BaseType_t status = pdFALSE;
|
||||
status = xSemaphoreGive(inst->event_obj->resource_hdl);
|
||||
@ -155,12 +156,12 @@ void mb_port_evt_res_release(mb_port_base_t *inst)
|
||||
}
|
||||
}
|
||||
|
||||
void mb_port_evt_set_resp_flag(mb_port_base_t *inst, mb_event_enum_t event_mask)
|
||||
void mb_port_event_set_resp_flag(mb_port_base_t *inst, mb_event_enum_t event_mask)
|
||||
{
|
||||
(void)xEventGroupSetBits(inst->event_obj->event_group_hdl, event_mask);
|
||||
}
|
||||
|
||||
mb_err_enum_t mb_port_evt_wait_req_finish(mb_port_base_t *inst)
|
||||
mb_err_enum_t mb_port_event_wait_req_finish(mb_port_base_t *inst)
|
||||
{
|
||||
mb_err_enum_t err_status = MB_ENOERR;
|
||||
mb_event_enum_t rcv_event;
|
||||
@ -192,7 +193,7 @@ mb_err_enum_t mb_port_evt_wait_req_finish(mb_port_base_t *inst)
|
||||
return err_status;
|
||||
}
|
||||
|
||||
void mb_port_evt_delete(mb_port_base_t *inst)
|
||||
void mb_port_event_delete(mb_port_base_t *inst)
|
||||
{
|
||||
if (inst->event_obj->resource_hdl) {
|
||||
vSemaphoreDelete(inst->event_obj->resource_hdl);
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include "port_common.h"
|
||||
|
||||
/* ----------------------- Variables ----------------------------------------*/
|
||||
static uint32_t inst_counter = 0;
|
||||
static _Atomic uint32_t inst_counter = 0;
|
||||
|
||||
/* ----------------------- Start implementation -----------------------------*/
|
||||
int lock_obj(_lock_t *plock)
|
||||
@ -58,6 +58,7 @@ QueueHandle_t queue_create(int queue_size)
|
||||
|
||||
void queue_delete(QueueHandle_t queue)
|
||||
{
|
||||
queue_flush(queue);
|
||||
vQueueDelete(queue);
|
||||
}
|
||||
|
||||
@ -65,7 +66,7 @@ esp_err_t queue_push(QueueHandle_t queue, void *pbuf, size_t len, frame_entry_t
|
||||
{
|
||||
frame_entry_t frame_info = {0};
|
||||
|
||||
if (!queue || !pbuf || (len <= 0)) {
|
||||
if (!queue) { // || !pbuf || (len <= 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -73,12 +74,16 @@ esp_err_t queue_push(QueueHandle_t queue, void *pbuf, size_t len, frame_entry_t
|
||||
frame_info = *pframe;
|
||||
}
|
||||
|
||||
frame_info.pbuf = calloc(1, len + 1);
|
||||
if (!frame_info.pbuf) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
if (pbuf && (len > 0)) {
|
||||
if (!frame_info.pbuf) {
|
||||
frame_info.pbuf = calloc(1, len);
|
||||
}
|
||||
if (!frame_info.pbuf) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
frame_info.len = len;
|
||||
memcpy(frame_info.pbuf, pbuf, len);
|
||||
}
|
||||
frame_info.len = len;
|
||||
memcpy(frame_info.pbuf, pbuf, len);
|
||||
|
||||
// try send to queue and check if the queue is full
|
||||
if (xQueueSend(queue, &frame_info, portMAX_DELAY) != pdTRUE) {
|
||||
@ -100,10 +105,13 @@ ssize_t queue_pop(QueueHandle_t queue, void *pbuf, size_t len, frame_entry_t *pf
|
||||
if (len > frame_info.len) {
|
||||
len = frame_info.len;
|
||||
}
|
||||
// if the input buffer pointer is defined copy the data and free
|
||||
// if the input buffer pointer is defined copy the data and free queued buffer,
|
||||
// otherwise just return the frame entry
|
||||
if (frame_info.pbuf && pbuf) {
|
||||
memcpy(pbuf, frame_info.pbuf, len);
|
||||
free(frame_info.pbuf);
|
||||
if (!pframe) {
|
||||
free(frame_info.pbuf); // must free the buffer manually!
|
||||
}
|
||||
}
|
||||
} else {
|
||||
goto err;
|
||||
|
@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
/*----------------------- Platform includes --------------------------------*/
|
||||
#include <stdatomic.h>
|
||||
#include "esp_idf_version.h"
|
||||
#include "esp_attr.h"
|
||||
|
||||
@ -25,19 +26,19 @@
|
||||
/* ----------------------- Defines ----------------------------------------*/
|
||||
struct mb_port_timer_t
|
||||
{
|
||||
spinlock_t spin_lock;
|
||||
//spinlock_t spin_lock;
|
||||
esp_timer_handle_t timer_handle;
|
||||
uint16_t t35_ticks;
|
||||
uint32_t response_time_ms;
|
||||
bool timer_state;
|
||||
uint16_t timer_mode;
|
||||
_Atomic uint32_t response_time_ms;
|
||||
_Atomic bool timer_state;
|
||||
_Atomic uint16_t timer_mode;
|
||||
};
|
||||
|
||||
/* ----------------------- Static variables ---------------------------------*/
|
||||
static const char *TAG = "mb_port.timer";
|
||||
|
||||
/* ----------------------- Start implementation -----------------------------*/
|
||||
mb_tmr_mode_enum_t mb_port_get_cur_tmr_mode(mb_port_base_t *inst);
|
||||
mb_timer_mode_enum_t mb_port_get_cur_timer_mode(mb_port_base_t *inst);
|
||||
|
||||
static void IRAM_ATTR timer_alarm_cb(void *param)
|
||||
{
|
||||
@ -45,11 +46,11 @@ static void IRAM_ATTR timer_alarm_cb(void *param)
|
||||
if (inst->cb.tmr_expired && inst->arg) {
|
||||
inst->cb.tmr_expired(inst->arg); // Timer expired callback function
|
||||
}
|
||||
inst->timer_obj->timer_state = true;
|
||||
ESP_EARLY_LOGD(TAG, "timer mode: (%d) triggered", mb_port_get_cur_tmr_mode(inst));
|
||||
atomic_store(&(inst->timer_obj->timer_state), true);
|
||||
ESP_EARLY_LOGD(TAG, "timer mode: (%d) triggered", mb_port_get_cur_timer_mode(inst));
|
||||
}
|
||||
|
||||
mb_err_enum_t mb_port_tmr_create(mb_port_base_t *inst, uint16_t t35_timer_ticks)
|
||||
mb_err_enum_t mb_port_timer_create(mb_port_base_t *inst, uint16_t t35_timer_ticks)
|
||||
{
|
||||
MB_RETURN_ON_FALSE((t35_timer_ticks > 0), MB_EILLSTATE, TAG,
|
||||
"modbus timeout discreet is incorrect.");
|
||||
@ -58,10 +59,11 @@ mb_err_enum_t mb_port_tmr_create(mb_port_base_t *inst, uint16_t t35_timer_ticks)
|
||||
mb_err_enum_t ret = MB_EILLSTATE;
|
||||
inst->timer_obj = (mb_port_timer_t *)calloc(1, sizeof(mb_port_timer_t));
|
||||
MB_GOTO_ON_FALSE((inst && inst->timer_obj), MB_EILLSTATE, error, TAG, "mb timer allocation error.");
|
||||
SPIN_LOCK_INIT(inst->timer_obj->spin_lock);
|
||||
inst->timer_obj->timer_handle = NULL;
|
||||
atomic_init(&(inst->timer_obj->timer_mode), MB_TMODE_T35);
|
||||
atomic_init(&(inst->timer_obj->timer_state), false);
|
||||
// Set default response time according to kconfig
|
||||
inst->timer_obj->response_time_ms = MB_MASTER_TIMEOUT_MS_RESPOND;
|
||||
atomic_init(&(inst->timer_obj->response_time_ms), MB_MASTER_TIMEOUT_MS_RESPOND);
|
||||
// Save timer reload value for Modbus T35 period
|
||||
inst->timer_obj->t35_ticks = t35_timer_ticks;
|
||||
esp_timer_create_args_t timer_conf = {
|
||||
@ -90,7 +92,7 @@ error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void mb_port_tmr_delete(mb_port_base_t *inst)
|
||||
void mb_port_timer_delete(mb_port_base_t *inst)
|
||||
{
|
||||
// Delete active timer
|
||||
if (inst->timer_obj)
|
||||
@ -105,72 +107,66 @@ void mb_port_tmr_delete(mb_port_base_t *inst)
|
||||
}
|
||||
}
|
||||
|
||||
void mb_port_tmr_us(mb_port_base_t *inst, uint64_t timeout_us)
|
||||
void mb_port_timer_us(mb_port_base_t *inst, uint64_t timeout_us)
|
||||
{
|
||||
MB_RETURN_ON_FALSE((inst && inst->timer_obj->timer_handle), ;, TAG, "timer is not initialized.");
|
||||
MB_RETURN_ON_FALSE((timeout_us > 0), ;, TAG,
|
||||
"%s, incorrect tick value for timer = (%" PRId64 ").", inst->descr.parent_name, timeout_us);
|
||||
esp_timer_stop(inst->timer_obj->timer_handle);
|
||||
esp_timer_start_once(inst->timer_obj->timer_handle, timeout_us);
|
||||
SPIN_LOCK_ENTER(inst->timer_obj->spin_lock);
|
||||
inst->timer_obj->timer_state = false;
|
||||
SPIN_LOCK_EXIT(inst->timer_obj->spin_lock);
|
||||
atomic_store(&(inst->timer_obj->timer_state), false);
|
||||
}
|
||||
|
||||
|
||||
inline void mb_port_set_cur_tmr_mode(mb_port_base_t *inst, mb_tmr_mode_enum_t tmr_mode)
|
||||
inline void mb_port_set_cur_timer_mode(mb_port_base_t *inst, mb_timer_mode_enum_t tmr_mode)
|
||||
{
|
||||
SPIN_LOCK_ENTER(inst->timer_obj->spin_lock);
|
||||
inst->timer_obj->timer_mode = tmr_mode;
|
||||
SPIN_LOCK_EXIT(inst->timer_obj->spin_lock);
|
||||
atomic_store(&(inst->timer_obj->timer_mode), tmr_mode);
|
||||
}
|
||||
|
||||
|
||||
inline mb_tmr_mode_enum_t mb_port_get_cur_tmr_mode(mb_port_base_t *inst)
|
||||
inline mb_timer_mode_enum_t mb_port_get_cur_timer_mode(mb_port_base_t *inst)
|
||||
{
|
||||
return inst->timer_obj->timer_mode;
|
||||
return atomic_load(&(inst->timer_obj->timer_mode));
|
||||
}
|
||||
|
||||
void mb_port_tmr_enable(mb_port_base_t *inst)
|
||||
void mb_port_timer_enable(mb_port_base_t *inst)
|
||||
{
|
||||
uint64_t tout_us = (inst->timer_obj->t35_ticks * MB_TIMER_TICK_TIME_US);
|
||||
|
||||
// Set current timer mode, don't change it.
|
||||
mb_port_set_cur_tmr_mode(inst, MB_TMODE_T35);
|
||||
mb_port_set_cur_timer_mode(inst, MB_TMODE_T35);
|
||||
// Set timer alarm
|
||||
mb_port_tmr_us(inst, tout_us);
|
||||
ESP_LOGW(TAG, "%s, start timer (%" PRIu64 ").", inst->descr.parent_name, tout_us);
|
||||
mb_port_timer_us(inst, tout_us);
|
||||
ESP_LOGD(TAG, "%s, start timer (%" PRIu64 ").", inst->descr.parent_name, tout_us);
|
||||
}
|
||||
|
||||
void mb_port_tmr_convert_delay_enable(mb_port_base_t *inst)
|
||||
void mb_port_timer_convert_delay_enable(mb_port_base_t *inst)
|
||||
{
|
||||
// Covert time in milliseconds into ticks
|
||||
uint64_t tout_us = (MB_MASTER_DELAY_MS_CONVERT * 1000);
|
||||
|
||||
// Set current timer mode
|
||||
mb_port_set_cur_tmr_mode(inst, MB_TMODE_CONVERT_DELAY);
|
||||
mb_port_set_cur_timer_mode(inst, MB_TMODE_CONVERT_DELAY);
|
||||
ESP_LOGD(TAG, "%s, convert delay enable.", inst->descr.parent_name);
|
||||
mb_port_tmr_us(inst, tout_us);
|
||||
mb_port_timer_us(inst, tout_us);
|
||||
}
|
||||
|
||||
void mb_port_tmr_respond_timeout_enable(mb_port_base_t *inst)
|
||||
void mb_port_timer_respond_timeout_enable(mb_port_base_t *inst)
|
||||
{
|
||||
uint64_t tout_us = (inst->timer_obj->response_time_ms * 1000);
|
||||
|
||||
mb_port_set_cur_tmr_mode(inst, MB_TMODE_RESPOND_TIMEOUT);
|
||||
ESP_LOGW(TAG, "%s, respond enable timeout (%d).",
|
||||
inst->descr.parent_name, (int)inst->timer_obj->response_time_ms);
|
||||
mb_port_tmr_us(inst, tout_us);
|
||||
mb_port_set_cur_timer_mode(inst, MB_TMODE_RESPOND_TIMEOUT);
|
||||
ESP_LOGD(TAG, "%s, respond enable timeout (%u).",
|
||||
inst->descr.parent_name, (unsigned)mb_port_timer_get_response_time_ms(inst));
|
||||
mb_port_timer_us(inst, tout_us);
|
||||
}
|
||||
|
||||
void mb_port_tmr_delay(mb_port_base_t *inst, uint16_t timeout_ms)
|
||||
void mb_port_timer_delay(mb_port_base_t *inst, uint16_t timeout_ms)
|
||||
{
|
||||
uint64_t tout_us = (timeout_ms * 1000);
|
||||
mb_port_tmr_us(inst, tout_us);
|
||||
mb_port_timer_us(inst, tout_us);
|
||||
}
|
||||
|
||||
|
||||
void mb_port_tmr_disable(mb_port_base_t *inst)
|
||||
void mb_port_timer_disable(mb_port_base_t *inst)
|
||||
{
|
||||
// Disable timer alarm
|
||||
esp_err_t err = esp_timer_stop(inst->timer_obj->timer_handle);
|
||||
@ -183,14 +179,12 @@ void mb_port_tmr_disable(mb_port_base_t *inst)
|
||||
}
|
||||
}
|
||||
|
||||
void mb_port_tmr_set_response_time(mb_port_base_t *inst, uint32_t resp_time_ms)
|
||||
void mb_port_timer_set_response_time(mb_port_base_t *inst, uint32_t resp_time_ms)
|
||||
{
|
||||
SPIN_LOCK_ENTER(inst->timer_obj->spin_lock);
|
||||
inst->timer_obj->response_time_ms = resp_time_ms;
|
||||
SPIN_LOCK_EXIT(inst->timer_obj->spin_lock);
|
||||
atomic_store(&(inst->timer_obj->response_time_ms), resp_time_ms);
|
||||
}
|
||||
|
||||
uint32_t mb_port_tmr_get_response_time_ms(mb_port_base_t *inst)
|
||||
uint32_t mb_port_timer_get_response_time_ms(mb_port_base_t *inst)
|
||||
{
|
||||
return inst->timer_obj->response_time_ms;
|
||||
return atomic_load(&(inst->timer_obj->response_time_ms));
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ void mb_port_ser_enable(mb_port_base_t *inst)
|
||||
CRITICAL_SECTION (port_obj->base.lock) {
|
||||
atomic_store(&(port_obj->enabled), true);
|
||||
mb_port_ser_bus_sema_release(inst);
|
||||
ESP_LOGW(TAG, "%s, resume port.", port_obj->base.descr.parent_name);
|
||||
ESP_LOGD(TAG, "%s, resume port.", port_obj->base.descr.parent_name);
|
||||
// Resume receiver task from known position
|
||||
vTaskResume(port_obj->task_handle);
|
||||
}
|
||||
@ -129,7 +129,7 @@ void mb_port_ser_disable(mb_port_base_t *inst)
|
||||
CRITICAL_SECTION (port_obj->base.lock) {
|
||||
// Suspend port task by itself
|
||||
atomic_store(&(port_obj->enabled), false);
|
||||
ESP_LOGW(TAG, "%s, suspend port.", port_obj->base.descr.parent_name);
|
||||
ESP_LOGD(TAG, "%s, suspend port.", port_obj->base.descr.parent_name);
|
||||
}
|
||||
}
|
||||
|
||||
@ -150,7 +150,7 @@ static void mb_port_ser_task(void *p_args)
|
||||
ESP_LOGD(TAG, "%s, UART[%d] event:", port_obj->base.descr.parent_name, port_obj->ser_opts.port);
|
||||
switch(event.type) {
|
||||
case UART_DATA:
|
||||
ESP_LOGW(TAG, "%s, data event, len: %d.", port_obj->base.descr.parent_name, (int)event.size);
|
||||
ESP_LOGD(TAG, "%s, data event, len: %d.", port_obj->base.descr.parent_name, (int)event.size);
|
||||
// This flag set in the event means that no more
|
||||
// data received during configured timeout and UART TOUT feature is triggered
|
||||
if (event.timeout_flag) {
|
||||
@ -162,12 +162,12 @@ static void mb_port_ser_task(void *p_args)
|
||||
uart_get_buffered_data_len(port_obj->ser_opts.port, (unsigned int*)&event.size);
|
||||
port_obj->recv_length = (event.size < MB_BUFFER_SIZE) ? event.size : MB_BUFFER_SIZE;
|
||||
if (event.size <= MB_SER_PDU_SIZE_MIN) {
|
||||
ESP_LOGW(TAG, "%s, drop short packet %d byte(s)", port_obj->base.descr.parent_name, (int)event.size);
|
||||
ESP_LOGD(TAG, "%s, drop short packet %d byte(s)", port_obj->base.descr.parent_name, (int)event.size);
|
||||
(void)mb_port_ser_rx_flush(&port_obj->base);
|
||||
break;
|
||||
}
|
||||
// New frame is received, send an event to main FSM to read it into receiver buffer
|
||||
mb_port_evt_post(&port_obj->base, EVENT(EV_FRAME_RECEIVED, port_obj->recv_length, NULL, 0));
|
||||
mb_port_event_post(&port_obj->base, EVENT(EV_FRAME_RECEIVED, port_obj->recv_length, NULL, 0));
|
||||
ESP_LOGD(TAG, "%s, frame %d bytes is ready.", port_obj->base.descr.parent_name, (int)port_obj->recv_length);
|
||||
}
|
||||
break;
|
||||
@ -187,12 +187,12 @@ static void mb_port_ser_task(void *p_args)
|
||||
break;
|
||||
//Event of UART parity check error
|
||||
case UART_PARITY_ERR:
|
||||
ESP_LOGW(TAG, "%s, uart parity error.", port_obj->base.descr.parent_name);
|
||||
ESP_LOGD(TAG, "%s, uart parity error.", port_obj->base.descr.parent_name);
|
||||
(void)mb_port_ser_rx_flush(&port_obj->base);
|
||||
break;
|
||||
//Event of UART frame error
|
||||
case UART_FRAME_ERR:
|
||||
ESP_LOGW(TAG, "%s, uart frame error.", port_obj->base.descr.parent_name);
|
||||
ESP_LOGD(TAG, "%s, uart frame error.", port_obj->base.descr.parent_name);
|
||||
(void)mb_port_ser_rx_flush(&port_obj->base);
|
||||
break;
|
||||
default:
|
||||
@ -293,21 +293,21 @@ bool mb_port_ser_recv_data(mb_port_base_t *inst, uint8_t **pp_ser_frame, uint16_
|
||||
uint16_t counter = *p_ser_length ? *p_ser_length : port_obj->recv_length;
|
||||
bool status = false;
|
||||
|
||||
status = mb_port_ser_bus_sema_take(inst, pdMS_TO_TICKS(mb_port_tmr_get_response_time_ms(inst)));
|
||||
status = mb_port_ser_bus_sema_take(inst, pdMS_TO_TICKS(mb_port_timer_get_response_time_ms(inst)));
|
||||
if (status && counter && *pp_ser_frame && atomic_load(&(port_obj->enabled))) {
|
||||
// Read frame data from the ringbuffer of receiver
|
||||
counter = uart_read_bytes(port_obj->ser_opts.port, (uint8_t *)*pp_ser_frame,
|
||||
counter, MB_SERIAL_RX_TOUT_TICKS);
|
||||
// Stop timer because the new data is received
|
||||
mb_port_tmr_disable(inst);
|
||||
mb_port_timer_disable(inst);
|
||||
// Store the timestamp of received frame
|
||||
port_obj->recv_time_stamp = esp_timer_get_time();
|
||||
ESP_LOGW(TAG, "%s, received data: %d bytes.", inst->descr.parent_name, (int)counter);
|
||||
ESP_LOG_BUFFER_HEX_LEVEL(MB_STR_CAT(inst->descr.parent_name, ":PORT_RECV"), (void *)*pp_ser_frame, counter, ESP_LOG_WARN);
|
||||
ESP_LOGD(TAG, "%s, received data: %d bytes.", inst->descr.parent_name, (int)counter);
|
||||
ESP_LOG_BUFFER_HEX_LEVEL(MB_STR_CAT(inst->descr.parent_name, ":PORT_RECV"), (void *)*pp_ser_frame, counter, ESP_LOG_DEBUG);
|
||||
int64_t time_delta = (port_obj->recv_time_stamp > port_obj->send_time_stamp) ?
|
||||
(port_obj->recv_time_stamp - port_obj->send_time_stamp) :
|
||||
(port_obj->send_time_stamp - port_obj->recv_time_stamp);
|
||||
ESP_LOGW(TAG, "%s, serial processing time[us] = %" PRId64, inst->descr.parent_name, time_delta);
|
||||
ESP_LOGD(TAG, "%s, serial processing time[us] = %" PRId64, inst->descr.parent_name, time_delta);
|
||||
status = true;
|
||||
*p_ser_length = counter;
|
||||
} else {
|
||||
@ -324,19 +324,19 @@ bool mb_port_ser_send_data(mb_port_base_t *inst, uint8_t *p_ser_frame, uint16_t
|
||||
int count = 0;
|
||||
mb_ser_port_t *port_obj = __containerof(inst, mb_ser_port_t, base);
|
||||
|
||||
res = mb_port_ser_bus_sema_take(inst, pdMS_TO_TICKS(mb_port_tmr_get_response_time_ms(inst)));
|
||||
res = mb_port_ser_bus_sema_take(inst, pdMS_TO_TICKS(mb_port_timer_get_response_time_ms(inst)));
|
||||
if (res && p_ser_frame && ser_length && atomic_load(&(port_obj->enabled))) {
|
||||
// Flush buffer received from previous transaction
|
||||
mb_port_ser_rx_flush(inst);
|
||||
count = uart_write_bytes(port_obj->ser_opts.port, p_ser_frame, ser_length);
|
||||
// Waits while UART sending the packet
|
||||
esp_err_t status = uart_wait_tx_done(port_obj->ser_opts.port, MB_SERIAL_TX_TOUT_TICKS);
|
||||
(void)mb_port_evt_post(inst, EVENT(EV_FRAME_SENT));
|
||||
(void)mb_port_event_post(inst, EVENT(EV_FRAME_SENT));
|
||||
ESP_LOGD(TAG, "%s, tx buffer sent: (%d) bytes.", inst->descr.parent_name, (int)count);
|
||||
MB_RETURN_ON_FALSE((status == ESP_OK), false, TAG, "%s, mb serial sent buffer failure.",
|
||||
inst->descr.parent_name);
|
||||
ESP_LOG_BUFFER_HEX_LEVEL(MB_STR_CAT(inst->descr.parent_name, ":PORT_SEND"),
|
||||
(void *)p_ser_frame, ser_length, ESP_LOG_WARN);
|
||||
(void *)p_ser_frame, ser_length, ESP_LOG_DEBUG);
|
||||
port_obj->send_time_stamp = esp_timer_get_time();
|
||||
} else {
|
||||
ESP_LOGE(TAG, "%s, send fail state:%d, %p, %u. ", inst->descr.parent_name, (int)port_obj->tx_state_en, p_ser_frame, (unsigned)ser_length);
|
||||
|
@ -17,8 +17,8 @@ extern "C" {
|
||||
#if (CONFIG_FMB_COMM_MODE_TCP_EN)
|
||||
|
||||
#define MB_TCP_PORT_MAX_CONN (CONFIG_FMB_TCP_PORT_MAX_CONN)
|
||||
#define MB_TCP_DEFAULT_PORT (502)
|
||||
#define MB_FRAME_QUEUE_SZ (10)
|
||||
#define MB_TCP_DEFAULT_PORT (CONFIG_FMB_TCP_PORT_DEFAULT)
|
||||
#define MB_FRAME_QUEUE_SZ (20)
|
||||
#define MB_TCP_CONNECTION_TIMEOUT_MS (20) // connection timeout in mS
|
||||
#define MB_TCP_RECONNECT_TIMEOUT (5000000) // reconnection timeout in uS
|
||||
|
||||
@ -30,7 +30,7 @@ extern "C" {
|
||||
buffer[(field) + 1] = (uint8_t)((val) & 0xFF); \
|
||||
}
|
||||
|
||||
#define MB_SLAVE_FMT(fmt) "slave #%d, socket(#%d)(%s)" fmt
|
||||
#define MB_NODE_FMT(fmt) "node #%d, socket(#%d)(%s)" fmt
|
||||
|
||||
mb_err_enum_t mbm_port_tcp_create(mb_tcp_opts_t *tcp_opts, mb_port_base_t **port_obj);
|
||||
void mbm_port_tcp_delete(mb_port_base_t *inst);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -31,21 +31,25 @@ extern "C" {
|
||||
|
||||
#if (CONFIG_FMB_COMM_MODE_TCP_EN)
|
||||
|
||||
#define MB_PORT_DEFAULT (502)
|
||||
#define INVALID_FD (-1)
|
||||
#define MB_PORT_DEFAULT (CONFIG_FMB_TCP_PORT_DEFAULT)
|
||||
#define UNDEF_FD (-1)
|
||||
#define MB_EVENT_TOUT (300 / portTICK_PERIOD_MS)
|
||||
#define MB_CONN_TICK_TIMEOUT (10 / portTICK_PERIOD_MS)
|
||||
|
||||
#define EVENT_HANDLER(handler_name) void handler_name(void *ctx, esp_event_base_t base, int32_t id, void *data)
|
||||
typedef void (*mb_event_handler_fp)(void *ctx, esp_event_base_t base, int32_t id, void *data);
|
||||
#define MB_EVENT_HANDLER(handler_name) void (handler_name)(void *ctx, esp_event_base_t base, int32_t id, void *data)
|
||||
|
||||
#define MB_TASK_STACK_SZ (CONFIG_FMB_PORT_TASK_STACK_SIZE)
|
||||
#define MB_TASK_PRIO (CONFIG_FMB_PORT_TASK_PRIO)
|
||||
#define MB_PORT_TASK_AFFINITY (CONFIG_FMB_PORT_TASK_AFFINITY)
|
||||
|
||||
#define MB_MAX_FDS (MB_TCP_PORT_MAX_CONN)
|
||||
#define MB_RETRY_MAX (2)
|
||||
#define MB_RECONNECT_TIME_MS (1000)
|
||||
#define MB_RX_QUEUE_MAX_SIZE (CONFIG_FMB_QUEUE_LENGTH)
|
||||
#define MB_TX_QUEUE_MAX_SIZE (CONFIG_FMB_QUEUE_LENGTH)
|
||||
#define MB_EVENT_QUEUE_SZ (CONFIG_FMB_QUEUE_LENGTH * MB_TCP_PORT_MAX_CONN)
|
||||
#define MB_TASK_STACK_SZ (CONFIG_FMB_PORT_TASK_STACK_SIZE)
|
||||
#define MB_TASK_PRIO (CONFIG_FMB_PORT_TASK_PRIO)
|
||||
#define MB_PORT_TASK_AFFINITY (CONFIG_FMB_PORT_TASK_AFFINITY)
|
||||
|
||||
#define MB_WAIT_DONE_MS (5000)
|
||||
#define MB_SELECT_WAIT_MS (200)
|
||||
#define MB_TCP_SEND_TIMEOUT_MS (500)
|
||||
@ -53,47 +57,86 @@ extern "C" {
|
||||
|
||||
#define MB_DRIVER_CONFIG_DEFAULT { \
|
||||
.spin_lock = portMUX_INITIALIZER_UNLOCKED, \
|
||||
.listen_sock_fd = UNDEF_FD, \
|
||||
.retry_cnt = MB_RETRY_MAX, \
|
||||
.mb_tcp_task_handle = NULL, \
|
||||
.mb_slave_open_count = 0, \
|
||||
.curr_slave_index = 0, \
|
||||
.mb_node_open_count = 0, \
|
||||
.curr_node_index = 0, \
|
||||
.mb_proto = MB_TCP, \
|
||||
.network_iface_ptr = NULL, \
|
||||
.mb_slave_info = NULL, \
|
||||
.mb_slave_curr_info = NULL, \
|
||||
.dns_name = NULL, \
|
||||
.mb_nodes = NULL, \
|
||||
.mb_node_curr = NULL, \
|
||||
.close_done_sema = NULL, \
|
||||
.max_conn_sd = INVALID_FD, \
|
||||
.slave_conn_count = 0, \
|
||||
.event_fd = INVALID_FD, \
|
||||
.max_conn_sd = UNDEF_FD, \
|
||||
.node_conn_count = 0, \
|
||||
.event_fd = UNDEF_FD, \
|
||||
}
|
||||
|
||||
#define MB_EVENTFD_CONFIG() (esp_vfs_eventfd_config_t) { \
|
||||
.max_fds = MB_TCP_PORT_MAX_CONN \
|
||||
#define MB_EVENTFD_CONFIG() (esp_vfs_eventfd_config_t) { \
|
||||
.max_fds = MB_TCP_PORT_MAX_CONN \
|
||||
};
|
||||
|
||||
typedef struct _port_driver port_driver_t;
|
||||
|
||||
#define MB_CHECK_FD_RANGE(fd) ((fd < MB_TCP_PORT_MAX_CONN) && (fd >= 0))
|
||||
|
||||
#define GET_CONFIG_PTR(ctx) (__extension__( \
|
||||
{ \
|
||||
assert(ctx); \
|
||||
((port_driver_t *)ctx); \
|
||||
} \
|
||||
#define MB_GET_DRV_PTR(ctx) (__extension__( \
|
||||
{ \
|
||||
assert(ctx); \
|
||||
((port_driver_t *)ctx); \
|
||||
} \
|
||||
))
|
||||
|
||||
#define MB_EVENT_TBL_IT(event) {event, #event}
|
||||
|
||||
#define MB_EVENT_BASE(context) (__extension__( \
|
||||
{ \
|
||||
port_driver_t *pdrv_ctx = GET_CONFIG_PTR(context); \
|
||||
(pdrv_ctx->loop_name) ? (esp_event_base_t)(pdrv_ctx->loop_name) : "UNK_BASE"; \
|
||||
} \
|
||||
#define MB_EVENT_BASE(context) (__extension__( \
|
||||
{ \
|
||||
port_driver_t *pdrv_ctx = MB_GET_DRV_PTR(context); \
|
||||
(pdrv_ctx->loop_name) ? (esp_event_base_t)(pdrv_ctx->loop_name) : "UNK_BASE"; \
|
||||
} \
|
||||
))
|
||||
|
||||
#define MB_GET_SLAVE_STATE(pslave) (atomic_load(&((mb_slave_info_t *)pslave)->addr_info.state))
|
||||
#define MB_ADD_FD(fd, max_fd, pfdset) do { \
|
||||
if (fd) { \
|
||||
(max_fd = (fd > max_fd) ? fd : max_fd); \
|
||||
FD_SET(fd, pfdset); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define MB_SET_SLAVE_STATE(pslave, slave_state) do { \
|
||||
atomic_store(&(((mb_slave_info_t *)pslave)->addr_info.state), slave_state); \
|
||||
|
||||
// Macro for atomic operations
|
||||
#define MB_ATOMIC_LOAD(ctx, addr) (__extension__( \
|
||||
{ \
|
||||
port_driver_t *pdrv_ctx = MB_GET_DRV_PTR(ctx); \
|
||||
(CRITICAL_LOAD(pdrv_ctx->lock, addr)); \
|
||||
} \
|
||||
))
|
||||
|
||||
#define MB_ATOMIC_STORE(ctx, addr, val) (__extension__( \
|
||||
{ \
|
||||
port_driver_t *pdrv_ctx = MB_GET_DRV_PTR(ctx); \
|
||||
CRITICAL_STORE(pdrv_ctx->lock, addr, val); \
|
||||
} \
|
||||
))
|
||||
|
||||
// Post event to event loop and unblocks the select through the eventfd to handle the event loop run,
|
||||
// So, the eventfd value keeps last event and its fd.
|
||||
#define DRIVER_SEND_EVENT(ctx, event, fd) (__extension__( \
|
||||
{ \
|
||||
port_driver_t *pdrv_ctx = MB_GET_DRV_PTR(ctx); \
|
||||
mb_event_info_t (event_info##__FUNCTION__##__LINE__); \
|
||||
(event_info##__FUNCTION__##__LINE__).event_id = (int32_t)event; \
|
||||
(event_info##__FUNCTION__##__LINE__).opt_fd = fd; \
|
||||
((write_event((void *)pdrv_ctx, &(event_info##__FUNCTION__##__LINE__)) > 0) \
|
||||
? ((event_info##__FUNCTION__##__LINE__)).event_id : UNDEF_FD); \
|
||||
} \
|
||||
))
|
||||
|
||||
#define MB_GET_NODE_STATE(pnode) (atomic_load(&((mb_node_info_t *)pnode)->addr_info.state))
|
||||
|
||||
#define MB_SET_NODE_STATE(pnode, node_state) do { \
|
||||
atomic_store(&(((mb_node_info_t *)pnode)->addr_info.state), node_state); \
|
||||
} while(0)
|
||||
|
||||
typedef enum _mb_driver_event {
|
||||
@ -103,7 +146,7 @@ typedef enum _mb_driver_event {
|
||||
MB_EVENT_CONNECT = 0x0008,
|
||||
MB_EVENT_SEND_DATA = 0x0010,
|
||||
MB_EVENT_RECV_DATA = 0x0020,
|
||||
MB_EVENT_RECONNECT = 0x0040,
|
||||
MB_EVENT_ERROR = 0x0040,
|
||||
MB_EVENT_CLOSE = 0x0080,
|
||||
MB_EVENT_TIMEOUT = 0x0100
|
||||
} mb_driver_event_t;
|
||||
@ -121,25 +164,13 @@ typedef union {
|
||||
uint64_t val;
|
||||
} mb_event_info_t;
|
||||
|
||||
// Post event to event loop and unblocks the select through the eventfd to handle the event loop run,
|
||||
// So, the eventfd value keeps last event and its fd.
|
||||
#define DRIVER_SEND_EVENT(ctx, event, fd) (__extension__( \
|
||||
{ \
|
||||
port_driver_t *pdrv_ctx = GET_CONFIG_PTR(ctx); \
|
||||
static mb_event_info_t event_info; \
|
||||
event_info.event_id = (int32_t)event; \
|
||||
event_info.opt_fd = fd; \
|
||||
(write_event((void *)pdrv_ctx, &event_info) > 0) ? event_info.event_id : -1; \
|
||||
} \
|
||||
))
|
||||
|
||||
typedef struct _mb_slave_info {
|
||||
typedef struct _mb_node_info {
|
||||
int index; /*!< slave information index */
|
||||
int fd; /*!< slave global file descriptor */
|
||||
int sock_id; /*!< socket ID of slave */
|
||||
mb_uid_info_t addr_info; /*!< slave address info structure*/
|
||||
int error; /*!< socket error */
|
||||
int recv_err; /*!< socket receive error */
|
||||
mb_uid_info_t addr_info; /*!< slave address info structure*/
|
||||
QueueHandle_t rx_queue; /*!< receive response queue */
|
||||
QueueHandle_t tx_queue; /*!< send request queue */
|
||||
int64_t send_time; /*!< send request time stamp */
|
||||
@ -148,7 +179,7 @@ typedef struct _mb_slave_info {
|
||||
uint16_t send_counter; /*!< number of packets sent to slave during one session */
|
||||
uint16_t recv_counter; /*!< number of packets received from slave during one session */
|
||||
bool is_blocking; /*!< slave blocking bit state saved */
|
||||
} mb_slave_info_t;
|
||||
} mb_node_info_t;
|
||||
|
||||
typedef enum _mb_sync_event {
|
||||
MB_SYNC_EVENT_RECV_OK = 0x0001,
|
||||
@ -158,57 +189,56 @@ typedef enum _mb_sync_event {
|
||||
} mb_sync_event_t;
|
||||
|
||||
typedef enum _mb_status_flags {
|
||||
MB_FLAG_DISCONNECTED = 0x0001,
|
||||
MB_FLAG_CONNECTED = 0x0002,
|
||||
MB_FLAG_SUSPEND = 0x0004,
|
||||
MB_FLAG_SHUTDOWN = 0x0008
|
||||
MB_FLAG_BLANK = 0x0000,
|
||||
MB_FLAG_TRANSACTION_DONE = 0x0001,
|
||||
MB_FLAG_DISCONNECTED = 0x0002,
|
||||
MB_FLAG_CONNECTED = 0x0004,
|
||||
MB_FLAG_SUSPEND = 0x0008,
|
||||
MB_FLAG_SHUTDOWN = 0x0010
|
||||
} mb_status_flags_t;
|
||||
|
||||
typedef struct _driver_event_cbs {
|
||||
void (*on_conn_done_cb)(void *);
|
||||
void *arg;
|
||||
void (*mb_sync_event_cb)(void *, mb_sync_event_t);
|
||||
uint64_t (*mb_sync_event_cb)(void *, mb_sync_event_t);
|
||||
void *port_arg;
|
||||
} mb_driver_event_cb_t;
|
||||
|
||||
/**
|
||||
* @brief Modbus slave addr list item for the master
|
||||
*/
|
||||
// typedef struct mb_uid_entry_s {
|
||||
// void* pinst;
|
||||
// mb_uid_info_t addr_info;
|
||||
// LIST_ENTRY(mb_uid_entry_s) entries; /*!< The slave address entry */
|
||||
// } mb_uid_entry_t;
|
||||
|
||||
/**
|
||||
* @brief Modbus driver context parameters
|
||||
*
|
||||
*/
|
||||
typedef struct _port_driver {
|
||||
void *parent; /*!< Parent object */
|
||||
portMUX_TYPE spin_lock; /*!< Driver spin lock */
|
||||
_lock_t lock; /*!< Driver semaphore mutex */
|
||||
bool is_registered; /*!< Driver is active flag */
|
||||
TaskHandle_t mb_tcp_task_handle; /*!< Master TCP/UDP handling task handle */
|
||||
mb_comm_mode_t mb_proto; /*!< Master protocol type */
|
||||
void *network_iface_ptr; /*!< Master netif interface pointer */
|
||||
mb_slave_info_t **mb_slave_info; /*!< Master information structure for each connected slave */
|
||||
uint16_t mb_slave_open_count; /*!< Master count of connected slaves */
|
||||
mb_slave_info_t *mb_slave_curr_info; /*!< Master current slave information */
|
||||
uint16_t curr_slave_index; /*!< Master current processing slave index */
|
||||
fd_set open_set; /*!< File descriptor set for opened slaves */
|
||||
fd_set conn_set; /*!< File descriptor set for connected slaves */
|
||||
EventGroupHandle_t status_flags_hdl; /*!< Status bits to control nodes states */
|
||||
int max_conn_sd; /*!< Max file descriptor for connected slaves */
|
||||
int slave_conn_count; /*!< Number of connected slaves */
|
||||
SemaphoreHandle_t close_done_sema; /*!< Close and done semaphore */
|
||||
void *parent; /*!< parent object pointer */
|
||||
char *dns_name; /*!< DNS name of the object */
|
||||
portMUX_TYPE spin_lock; /*!< spin lock */
|
||||
_lock_t lock; /*!< semaphore mutex */
|
||||
bool is_registered; /*!< driver is active flag */
|
||||
int listen_sock_fd; /*!< listen socket fd */
|
||||
int retry_cnt; /*!< retry counter for events */
|
||||
mb_comm_mode_t mb_proto; /*!< current node protocol type */
|
||||
uint16_t port; /*!< current node port number */
|
||||
uint8_t uid; /*!< unit identifier of the node */
|
||||
bool is_master; /*!< identify the type of instance (master, slave) */
|
||||
void *network_iface_ptr; /*!< netif interface pointer */
|
||||
mb_node_info_t **mb_nodes; /*!< information structures for each associated node */
|
||||
uint16_t mb_node_open_count; /*!< count of associated nodes */
|
||||
uint16_t node_conn_count; /*!< number of associated nodes */
|
||||
mb_node_info_t *mb_node_curr; /*!< current slave information */
|
||||
uint16_t curr_node_index; /*!< current processing slave index */
|
||||
fd_set open_set; /*!< file descriptor set for opened nodes */
|
||||
fd_set conn_set; /*!< file descriptor set for associated nodes */
|
||||
int max_conn_sd; /*!< max file descriptor for associated nodes */
|
||||
int event_fd; /*!< eventfd descriptor for modbus event tracking */
|
||||
SemaphoreHandle_t close_done_sema; /*!< close and done semaphore */
|
||||
EventGroupHandle_t status_flags_hdl; /*!< status bits to control nodes states */
|
||||
TaskHandle_t mb_tcp_task_handle; /*!< TCP/UDP handling task handle */
|
||||
esp_event_loop_handle_t event_loop_hdl; /*!< event loop handle */
|
||||
char *loop_name; /*!< name for event loop used as base */
|
||||
esp_event_handler_instance_t event_handler; /*!< event handler instance */
|
||||
char *loop_name; /*!< name for event loop used as base */
|
||||
mb_driver_event_cb_t event_cbs;
|
||||
//LIST_HEAD(mb_uid_info_, mb_uid_entry_s) slave_list; /*!< Slave address information list */
|
||||
uint16_t slave_list_count;
|
||||
//LIST_HEAD(mb_uid_info_, mb_uid_entry_s) node_list; /*!< node address information list */
|
||||
//uint16_t node_list_count;
|
||||
} port_driver_t;
|
||||
|
||||
/**
|
||||
@ -221,7 +251,7 @@ typedef struct _port_driver {
|
||||
* @return esp_err_t
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t mbm_drv_register(port_driver_t **config);
|
||||
esp_err_t mb_drv_register(port_driver_t **config);
|
||||
|
||||
/**
|
||||
* @brief Unregister modbus driver
|
||||
@ -230,7 +260,7 @@ esp_err_t mbm_drv_register(port_driver_t **config);
|
||||
* @return esp_err_t
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t mbm_drv_unregister(void *ctx);
|
||||
esp_err_t mb_drv_unregister(void *ctx);
|
||||
|
||||
/**
|
||||
* @brief Start task of modbus driver
|
||||
@ -239,7 +269,7 @@ esp_err_t mbm_drv_unregister(void *ctx);
|
||||
* @return esp_err_t
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t mbm_drv_start_task(void *ctx);
|
||||
esp_err_t mb_drv_start_task(void *ctx);
|
||||
|
||||
|
||||
/**
|
||||
@ -249,7 +279,7 @@ esp_err_t mbm_drv_start_task(void *ctx);
|
||||
* @return esp_err_t
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t mbm_drv_stop_task(void *ctx);
|
||||
esp_err_t mb_drv_stop_task(void *ctx);
|
||||
|
||||
/**
|
||||
* @brief get slave information structure from its short slave address
|
||||
@ -257,37 +287,49 @@ esp_err_t mbm_drv_stop_task(void *ctx);
|
||||
* This function must be called after initialization of ESP-MODBUS Interface
|
||||
*
|
||||
* @param uid - modbus slave address of the slave
|
||||
* @return mb_slave_info_t
|
||||
* @return mb_node_info_t
|
||||
* - Address of slave info structure on success
|
||||
* - NULL, if the slave is not found
|
||||
*/
|
||||
mb_slave_info_t *mbm_drv_get_slave_info_from_addr(void *ctx, uint8_t uid);
|
||||
mb_node_info_t *mb_drv_get_node_info_from_addr(void *ctx, uint8_t uid);
|
||||
|
||||
int mbm_drv_open(void *ctx, mb_uid_info_t addr_info, int flags);
|
||||
mb_node_info_t *mb_drv_get_node(void *ctx, int fd);
|
||||
|
||||
ssize_t mbm_drv_write(void *ctx, int fd, const void *data, size_t size);
|
||||
mb_sock_state_t mb_drv_get_node_state(void *ctx, int fd);
|
||||
|
||||
ssize_t mbm_drv_read(void *ctx, int fd, void *data, size_t size);
|
||||
int mb_drv_open(void *ctx, mb_uid_info_t addr_info, int flags);
|
||||
|
||||
int mbm_drv_close(void *ctx, int fd);
|
||||
ssize_t mb_drv_write(void *ctx, int fd, const void *data, size_t size);
|
||||
|
||||
ssize_t mb_drv_read(void *ctx, int fd, void *data, size_t size);
|
||||
|
||||
int mb_drv_close(void *ctx, int fd);
|
||||
|
||||
int32_t write_event(void *ctx, mb_event_info_t *pevent);
|
||||
|
||||
const char *driver_event_to_name_r(mb_driver_event_t event);
|
||||
|
||||
void mbm_drv_set_cb(void *ctx, void *conn_cb, void *arg);
|
||||
void mb_drv_set_cb(void *ctx, void *conn_cb, void *arg);
|
||||
|
||||
mb_status_flags_t mbm_drv_wait_status_flag(void *ctx, mb_status_flags_t mask, uint32_t tout_ms);
|
||||
mb_status_flags_t mb_drv_wait_status_flag(void *ctx, mb_status_flags_t mask, TickType_t ticks);
|
||||
|
||||
EVENT_HANDLER(on_ready);
|
||||
EVENT_HANDLER(on_open);
|
||||
EVENT_HANDLER(on_connect);
|
||||
EVENT_HANDLER(on_resolve);
|
||||
EVENT_HANDLER(on_send_data);
|
||||
EVENT_HANDLER(on_recv_data);
|
||||
EVENT_HANDLER(on_reconnect);
|
||||
EVENT_HANDLER(on_close);
|
||||
EVENT_HANDLER(on_timeout);
|
||||
esp_err_t mb_drv_register_handler(void *ctx, mb_driver_event_t event, mb_event_handler_fp fp);
|
||||
|
||||
esp_err_t mb_drv_unregister_handler(void *ctx, mb_driver_event_t event);
|
||||
|
||||
void mb_drv_check_suspend_shutdown(void *ctx);
|
||||
|
||||
void mb_drv_lock(void *ctx);
|
||||
|
||||
void mb_drv_unlock(void *ctx);
|
||||
|
||||
mb_node_info_t *mb_drv_get_next_node_from_set(void *ctx, int *pfd, fd_set *pfdset);
|
||||
|
||||
mb_status_flags_t mb_drv_set_status_flag(void *ctx, mb_status_flags_t mask);
|
||||
|
||||
mb_status_flags_t mb_drv_clear_status_flag(void *ctx, mb_status_flags_t mask);
|
||||
|
||||
err_t mb_drv_check_node_state(void *ctx, int fd);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
*/
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
@ -24,10 +24,81 @@ typedef struct
|
||||
|
||||
/* ----------------------- Static variables & functions ----------------------*/
|
||||
static const char *TAG = "mb_port.tcp.master";
|
||||
static void mbm_port_tcp_sync_event(void *inst, mb_sync_event_t sync_event);
|
||||
static uint64_t mbm_port_tcp_sync_event(void *inst, mb_sync_event_t sync_event);
|
||||
bool mbm_port_timer_expired(void *inst);
|
||||
extern int port_scan_addr_string(char *buffer, mb_uid_info_t *pslave_info);
|
||||
|
||||
static esp_err_t mbm_port_tcp_register_handlers(void *ctx)
|
||||
{
|
||||
port_driver_t *pdrv_ctx = MB_GET_DRV_PTR(ctx);
|
||||
esp_err_t ret = ESP_ERR_INVALID_STATE;
|
||||
|
||||
ret = mb_drv_register_handler(pdrv_ctx, MB_EVENT_READY, mbm_on_ready);
|
||||
MB_RETURN_ON_FALSE((ret == ESP_OK), MB_EINVAL, TAG,
|
||||
"%x, mb tcp port event registration failed.", (int)MB_EVENT_READY);
|
||||
ret = mb_drv_register_handler(pdrv_ctx, MB_EVENT_OPEN, mbm_on_open);
|
||||
MB_RETURN_ON_FALSE((ret == ESP_OK), MB_EINVAL, TAG,
|
||||
"%x, mb tcp port event registration failed.", (int)MB_EVENT_OPEN);
|
||||
ret = mb_drv_register_handler(pdrv_ctx, MB_EVENT_RESOLVE, mbm_on_resolve);
|
||||
MB_RETURN_ON_FALSE((ret == ESP_OK), MB_EINVAL, TAG,
|
||||
"%x, mb tcp port event registration failed.", (int)MB_EVENT_RESOLVE);
|
||||
ret = mb_drv_register_handler(pdrv_ctx, MB_EVENT_CONNECT, mbm_on_connect);
|
||||
MB_RETURN_ON_FALSE((ret == ESP_OK), MB_EINVAL, TAG,
|
||||
"%x, mb tcp port event registration failed.", (int)MB_EVENT_CONNECT);
|
||||
ret = mb_drv_register_handler(pdrv_ctx, MB_EVENT_ERROR, mbm_on_error);
|
||||
MB_RETURN_ON_FALSE((ret == ESP_OK), MB_EINVAL, TAG,
|
||||
"%x, mb tcp port event registration failed.", (int)MB_EVENT_ERROR);
|
||||
ret = mb_drv_register_handler(pdrv_ctx, MB_EVENT_SEND_DATA, mbm_on_send_data);
|
||||
MB_RETURN_ON_FALSE((ret == ESP_OK), MB_EINVAL, TAG,
|
||||
"%x, mb tcp port event registration failed.", (int)MB_EVENT_SEND_DATA);
|
||||
ret = mb_drv_register_handler(pdrv_ctx, MB_EVENT_RECV_DATA, mbm_on_recv_data);
|
||||
MB_RETURN_ON_FALSE((ret == ESP_OK), MB_EINVAL, TAG,
|
||||
"%x, mb tcp port event registration failed.", (int)MB_EVENT_RECV_DATA);
|
||||
ret = mb_drv_register_handler(pdrv_ctx, MB_EVENT_CLOSE, mbm_on_close);
|
||||
MB_RETURN_ON_FALSE((ret == ESP_OK), MB_EINVAL, TAG,
|
||||
"%x, mb tcp port event registration failed.", (int)MB_EVENT_CLOSE);
|
||||
ret = mb_drv_register_handler(pdrv_ctx, MB_EVENT_TIMEOUT, mbm_on_timeout);
|
||||
MB_RETURN_ON_FALSE((ret == ESP_OK), MB_EINVAL, TAG,
|
||||
"%x, mb tcp port event registration failed.", (int)MB_EVENT_TIMEOUT);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t mbm_port_tcp_unregister_handlers(void *ctx)
|
||||
{
|
||||
port_driver_t *pdrv_ctx = MB_GET_DRV_PTR(ctx);
|
||||
esp_err_t ret = ESP_ERR_INVALID_STATE;
|
||||
ESP_LOGD(TAG, "%p, event handler %p, unregister.", pdrv_ctx, pdrv_ctx->event_handler);
|
||||
|
||||
ret = mb_drv_unregister_handler(pdrv_ctx, MB_EVENT_READY);
|
||||
MB_RETURN_ON_FALSE((ret == ESP_OK), MB_EINVAL, TAG,
|
||||
"%x, mb tcp port event registration failed.", (int)MB_EVENT_READY);
|
||||
ret = mb_drv_unregister_handler(pdrv_ctx, MB_EVENT_OPEN);
|
||||
MB_RETURN_ON_FALSE((ret == ESP_OK), MB_EINVAL, TAG,
|
||||
"%x, mb tcp port event registration failed.", (int)MB_EVENT_OPEN);
|
||||
ret = mb_drv_unregister_handler(pdrv_ctx, MB_EVENT_RESOLVE);
|
||||
MB_RETURN_ON_FALSE((ret == ESP_OK), MB_EINVAL, TAG,
|
||||
"%x, mb tcp port event registration failed.", (int)MB_EVENT_RESOLVE);
|
||||
ret = mb_drv_unregister_handler(pdrv_ctx, MB_EVENT_CONNECT);
|
||||
MB_RETURN_ON_FALSE((ret == ESP_OK), MB_EINVAL, TAG,
|
||||
"%x, mb tcp port event registration failed.", (int)MB_EVENT_CONNECT);
|
||||
ret = mb_drv_unregister_handler(pdrv_ctx, MB_EVENT_ERROR);
|
||||
MB_RETURN_ON_FALSE((ret == ESP_OK), MB_EINVAL, TAG,
|
||||
"%x, mb tcp port event registration failed.", (int)MB_EVENT_ERROR);
|
||||
ret = mb_drv_unregister_handler(pdrv_ctx, MB_EVENT_SEND_DATA);
|
||||
MB_RETURN_ON_FALSE((ret == ESP_OK), MB_EINVAL, TAG,
|
||||
"%x, mb tcp port event registration failed.", (int)MB_EVENT_SEND_DATA);
|
||||
ret = mb_drv_unregister_handler(pdrv_ctx, MB_EVENT_RECV_DATA);
|
||||
MB_RETURN_ON_FALSE((ret == ESP_OK), MB_EINVAL, TAG,
|
||||
"%x, mb tcp port event registration failed.", (int)MB_EVENT_RECV_DATA);
|
||||
ret = mb_drv_unregister_handler(pdrv_ctx, MB_EVENT_CLOSE);
|
||||
MB_RETURN_ON_FALSE((ret == ESP_OK), MB_EINVAL, TAG,
|
||||
"%x, mb tcp port event registration failed.", (int)MB_EVENT_CLOSE);
|
||||
ret = mb_drv_unregister_handler(pdrv_ctx, MB_EVENT_TIMEOUT);
|
||||
MB_RETURN_ON_FALSE((ret == ESP_OK), MB_EINVAL, TAG,
|
||||
"%x, mb tcp port event registration failed.", (int)MB_EVENT_TIMEOUT);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
mb_err_enum_t mbm_port_tcp_create(mb_tcp_opts_t *tcp_opts, mb_port_base_t **port_obj)
|
||||
{
|
||||
MB_RETURN_ON_FALSE((port_obj && tcp_opts), MB_EINVAL, TAG, "mb tcp port invalid arguments.");
|
||||
@ -41,13 +112,21 @@ mb_err_enum_t mbm_port_tcp_create(mb_tcp_opts_t *tcp_opts, mb_port_base_t **port
|
||||
CRITICAL_SECTION_INIT(ptcp->base.lock);
|
||||
ptcp->base.descr = ((mb_port_base_t *)*port_obj)->descr;
|
||||
|
||||
err = mbm_drv_register(&ptcp->pdriver);
|
||||
err = mb_drv_register(&ptcp->pdriver);
|
||||
MB_GOTO_ON_FALSE(((err == ESP_OK) && ptcp->pdriver), MB_EILLSTATE, error,
|
||||
TAG, "mb tcp port driver registration failed.");
|
||||
TAG, "mb tcp port driver registration failed, err = (%x).", (int)err);
|
||||
ptcp->pdriver->parent = ptcp;
|
||||
|
||||
err = mbm_port_tcp_register_handlers(ptcp->pdriver);
|
||||
MB_GOTO_ON_FALSE(((err == ESP_OK) && ptcp->pdriver), MB_EILLSTATE, error,
|
||||
TAG, "mb tcp port driver event handlers registration failed, err = (%x).", (int)err);
|
||||
|
||||
ptcp->pdriver->network_iface_ptr = tcp_opts->ip_netif_ptr;
|
||||
ptcp->pdriver->mb_proto = tcp_opts->mode;
|
||||
ptcp->pdriver->port = tcp_opts->port;
|
||||
ptcp->pdriver->uid = tcp_opts->uid;
|
||||
ptcp->pdriver->is_master = true;
|
||||
ptcp->pdriver->dns_name = tcp_opts->dns_name;
|
||||
ptcp->pdriver->event_cbs.mb_sync_event_cb = mbm_port_tcp_sync_event;
|
||||
ptcp->pdriver->event_cbs.port_arg = (void *)ptcp;
|
||||
|
||||
@ -58,22 +137,21 @@ mb_err_enum_t mbm_port_tcp_create(mb_tcp_opts_t *tcp_opts, mb_port_base_t **port
|
||||
|
||||
char **paddr_table = tcp_opts->ip_addr_table;
|
||||
MB_GOTO_ON_FALSE((paddr_table && *paddr_table), MB_EILLSTATE, error,
|
||||
TAG, "mb tcp port driver registration failed.");
|
||||
TAG, "mb tcp port nodes registration failed %p, %p.", paddr_table, *paddr_table);
|
||||
mb_uid_info_t slave_address_info;
|
||||
int fd = 0;
|
||||
|
||||
// Just for test now
|
||||
while(*paddr_table) {
|
||||
int res = port_scan_addr_string((char *)*paddr_table, &slave_address_info);
|
||||
if (res > 0) {
|
||||
ESP_LOGW(TAG, "Config: %s, IP: %s, port: %d, slave_addr: %d, ip_ver: %s",
|
||||
ESP_LOGD(TAG, "Config: %s, IP: %s, port: %d, slave_addr: %d, ip_ver: %s",
|
||||
(char *)*paddr_table, slave_address_info.ip_addr_str, slave_address_info.port,
|
||||
slave_address_info.uid, (slave_address_info.addr_type == MB_IPV4 ? "IPV4" : "IPV6"));
|
||||
fd = mbm_drv_open(ptcp->pdriver, slave_address_info, 0);
|
||||
slave_address_info.uid, (slave_address_info.addr_type == MB_IPV4 ? "IPV4" : "IPV6"));
|
||||
fd = mb_drv_open(ptcp->pdriver, slave_address_info, 0);
|
||||
if (fd < 0) {
|
||||
ESP_LOGE(TAG, "%p, unable to open slave: %s", ptcp->pdriver, slave_address_info.ip_addr_str);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "%p, open slave: %d, %s:%d",
|
||||
ESP_LOGD(TAG, "%p, open slave: %d, %s:%d",
|
||||
ptcp->pdriver, fd, slave_address_info.ip_addr_str, slave_address_info.port);
|
||||
}
|
||||
} else {
|
||||
@ -81,14 +159,26 @@ mb_err_enum_t mbm_port_tcp_create(mb_tcp_opts_t *tcp_opts, mb_port_base_t **port
|
||||
}
|
||||
paddr_table++;
|
||||
}
|
||||
|
||||
#ifdef MB_MDNS_IS_INCLUDED
|
||||
err = port_start_mdns_service(&ptcp->pdriver->dns_name, true, tcp_opts->uid, ptcp->pdriver->network_iface_ptr);
|
||||
MB_GOTO_ON_FALSE((err == ESP_OK), MB_EILLSTATE, error,
|
||||
TAG, "mb tcp port mdns service init failure.");
|
||||
#endif
|
||||
|
||||
*port_obj = &(ptcp->base);
|
||||
ESP_LOGD(TAG, "created object @%p", ptcp);
|
||||
return MB_ENOERR;
|
||||
|
||||
error:
|
||||
#ifdef MB_MDNS_IS_INCLUDED
|
||||
port_stop_mdns_service(&ptcp->pdriver->dns_name);
|
||||
#endif
|
||||
if (ptcp && ptcp->pdriver) {
|
||||
(void)mbm_drv_unregister(ptcp->pdriver);
|
||||
(void)mbm_port_tcp_unregister_handlers(ptcp->pdriver);
|
||||
(void)mb_drv_unregister(ptcp->pdriver);
|
||||
CRITICAL_SECTION_CLOSE(ptcp->base.lock);
|
||||
// if the MDNS resolving is enabled, then free it
|
||||
}
|
||||
free(ptcp);
|
||||
return ret;
|
||||
@ -97,7 +187,13 @@ error:
|
||||
void mbm_port_tcp_delete(mb_port_base_t *inst)
|
||||
{
|
||||
mbm_tcp_port_t *port_obj = __containerof(inst, mbm_tcp_port_t, base);
|
||||
esp_err_t err = mbm_drv_unregister(port_obj->pdriver);
|
||||
esp_err_t err = MB_EILLSTATE;
|
||||
err = mbm_port_tcp_unregister_handlers(port_obj->pdriver);
|
||||
MB_RETURN_ON_FALSE((err == ESP_OK), ;, TAG, "mb tcp port invalid arguments.");
|
||||
#ifdef MB_MDNS_IS_INCLUDED
|
||||
port_stop_mdns_service(&port_obj->pdriver->dns_name);
|
||||
#endif
|
||||
err = mb_drv_unregister(port_obj->pdriver);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "driver unregister fail, returns (0x%d).", (uint16_t)err);
|
||||
}
|
||||
@ -108,51 +204,46 @@ void mbm_port_tcp_delete(mb_port_base_t *inst)
|
||||
void mbm_port_tcp_enable(mb_port_base_t *inst)
|
||||
{
|
||||
mbm_tcp_port_t *port_obj = __containerof(inst, mbm_tcp_port_t, base);
|
||||
//esp_err_t err = ESP_ERR_INVALID_STATE;
|
||||
// if (!port_obj->pdriver->is_registered && !port_obj->pdriver) {
|
||||
// err = mbm_drv_register(&port_obj->pdriver);
|
||||
// MB_RETURN_ON_FALSE((err == ESP_OK), ;, TAG, "mb tcp port driver register failed.");
|
||||
// }
|
||||
(void)mbm_drv_start_task(port_obj->pdriver);
|
||||
DRIVER_SEND_EVENT(port_obj->pdriver, MB_EVENT_RESOLVE, -1);
|
||||
(void)mb_drv_start_task(port_obj->pdriver);
|
||||
(void)mb_drv_clear_status_flag(port_obj->pdriver, MB_FLAG_DISCONNECTED);
|
||||
DRIVER_SEND_EVENT(port_obj->pdriver, MB_EVENT_RESOLVE, UNDEF_FD);
|
||||
}
|
||||
|
||||
void mbm_port_tcp_disable(mb_port_base_t *inst)
|
||||
{
|
||||
mbm_tcp_port_t *port_obj = __containerof(inst, mbm_tcp_port_t, base);
|
||||
// Change the state of all slaves to close
|
||||
DRIVER_SEND_EVENT(port_obj->pdriver, MB_EVENT_CLOSE, -1);
|
||||
(void)mbm_drv_wait_status_flag(port_obj->pdriver, MB_FLAG_DISCONNECTED, MB_RECONNECT_TIME_MS);
|
||||
//(void)mbm_drv_stop_task(port_obj->pdriver); // do not stop the task if we want to gracefully shutdown the task
|
||||
DRIVER_SEND_EVENT(port_obj->pdriver, MB_EVENT_CLOSE, UNDEF_FD);
|
||||
(void)mb_drv_wait_status_flag(port_obj->pdriver, MB_FLAG_DISCONNECTED, pdMS_TO_TICKS(MB_RECONNECT_TIME_MS));
|
||||
}
|
||||
|
||||
bool mbm_port_tcp_recv_data(mb_port_base_t *inst, uint8_t **ppframe, uint16_t *plength)
|
||||
{
|
||||
mbm_tcp_port_t *port_obj = __containerof(inst, mbm_tcp_port_t, base);
|
||||
|
||||
mb_slave_info_t *pinfo = port_obj->pdriver->mb_slave_curr_info;
|
||||
mb_node_info_t *pinfo = port_obj->pdriver->mb_node_curr;
|
||||
MB_RETURN_ON_FALSE((pinfo), false, TAG, "incorrect current slave pointer.");
|
||||
bool status = false;
|
||||
|
||||
size_t sz = mbm_drv_read(port_obj->pdriver, pinfo->fd, port_obj->ptemp_buf, MB_BUFFER_SIZE);
|
||||
size_t sz = mb_drv_read(port_obj->pdriver, pinfo->fd, port_obj->ptemp_buf, MB_BUFFER_SIZE);
|
||||
if (sz > MB_TCP_FUNC) {
|
||||
uint16_t tid_counter = MB_TCP_MBAP_GET_FIELD(port_obj->ptemp_buf, MB_TCP_TID);
|
||||
if (tid_counter == (pinfo->tid_counter - 1)) {
|
||||
*ppframe = port_obj->ptemp_buf;
|
||||
*plength = sz;
|
||||
ESP_LOGW(TAG, "%p, "MB_SLAVE_FMT(", received packet TID = 0x%.4x:(0x%.4x), %p."),
|
||||
ESP_LOGD(TAG, "%p, "MB_NODE_FMT(", get packet TID: 0x%04" PRIx16 ":0x%04" PRIx16 ", %p."),
|
||||
port_obj->pdriver, pinfo->index, pinfo->sock_id, pinfo->addr_info.ip_addr_str,
|
||||
tid_counter, pinfo->tid_counter, *ppframe);
|
||||
(unsigned)tid_counter, (unsigned)pinfo->tid_counter, *ppframe);
|
||||
|
||||
uint64_t time = 0;
|
||||
time = port_get_timestamp() - pinfo->send_time;
|
||||
ESP_LOGW(TAG, "%p, "MB_SLAVE_FMT(", processing time[us] = %ju."), port_obj->pdriver, pinfo->index,
|
||||
ESP_LOGD(TAG, "%p, "MB_NODE_FMT(", processing time[us] = %ju."), port_obj->pdriver, pinfo->index,
|
||||
pinfo->sock_id, pinfo->addr_info.ip_addr_str, time);
|
||||
status = true;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "%p, "MB_SLAVE_FMT(", drop packet TID = 0x%.4x:0x%.4x, %p."),
|
||||
ESP_LOGE(TAG, "%p, "MB_NODE_FMT(", drop packet TID: 0x%04" PRIx16 ":0x%04" PRIx16 ", %p."),
|
||||
port_obj->pdriver, pinfo->index, pinfo->sock_id,
|
||||
pinfo->addr_info.ip_addr_str, tid_counter, pinfo->tid_counter, *ppframe);
|
||||
pinfo->addr_info.ip_addr_str, (unsigned)tid_counter, (unsigned)pinfo->tid_counter, *ppframe);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
@ -164,8 +255,8 @@ bool mbm_port_tcp_send_data(mb_port_base_t *inst, uint8_t address, uint8_t *pfra
|
||||
|
||||
bool frame_sent = false;
|
||||
// get slave descriptor from its address
|
||||
mb_slave_info_t *pinfo = (mb_slave_info_t *)mbm_drv_get_slave_info_from_addr(port_obj->pdriver, address);
|
||||
MB_RETURN_ON_FALSE((pinfo && (MB_GET_SLAVE_STATE(pinfo) >= MB_SOCK_STATE_CONNECTED)),
|
||||
mb_node_info_t *pinfo = (mb_node_info_t *)mb_drv_get_node_info_from_addr(port_obj->pdriver, address);
|
||||
MB_RETURN_ON_FALSE((pinfo && (MB_GET_NODE_STATE(pinfo) >= MB_SOCK_STATE_CONNECTED)),
|
||||
false, TAG, "the slave address #%d is not registered.", address);
|
||||
|
||||
if (pinfo && pframe) {
|
||||
@ -174,17 +265,17 @@ bool mbm_port_tcp_send_data(mb_port_base_t *inst, uint8_t address, uint8_t *pfra
|
||||
pframe[MB_TCP_UID] = (uint8_t)(pinfo->addr_info.uid);
|
||||
}
|
||||
|
||||
ESP_LOGW(TAG, "%p, send fd: %d, sock_id: %d[%s], %p, len: %d",
|
||||
ESP_LOGD(TAG, "%p, send fd: %d, sock_id: %d[%s], %p, len: %d",
|
||||
port_obj->pdriver, pinfo->fd, pinfo->sock_id, pinfo->addr_info.node_name_str, pframe, length);
|
||||
|
||||
// Write data to the modbus vfs driver send queue of the slave
|
||||
int write_length = mbm_drv_write(port_obj->pdriver, pinfo->fd, pframe, length);
|
||||
// Write data to the modbus driver send queue of the slave
|
||||
int write_length = mb_drv_write(port_obj->pdriver, pinfo->fd, pframe, length);
|
||||
if (write_length) {
|
||||
frame_sent = true;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "mbm_write fail, returns %d.", write_length);
|
||||
}
|
||||
// mb_port_tmr_respond_timeout_enable(inst); // the timer is set in the transport
|
||||
// mb_port_timer_respond_timeout_enable(inst); // the timer is set in the transport
|
||||
|
||||
return frame_sent;
|
||||
}
|
||||
@ -192,7 +283,7 @@ bool mbm_port_tcp_send_data(mb_port_base_t *inst, uint8_t address, uint8_t *pfra
|
||||
void mbm_port_tcp_set_conn_cb(mb_port_base_t *inst, void *conn_fp, void *arg)
|
||||
{
|
||||
mbm_tcp_port_t *port_obj = __containerof(inst, mbm_tcp_port_t, base);
|
||||
mbm_drv_set_cb(port_obj->pdriver, conn_fp, arg);
|
||||
mb_drv_set_cb(port_obj->pdriver, conn_fp, arg);
|
||||
}
|
||||
|
||||
// Timer handler to check timeout of socket response
|
||||
@ -204,20 +295,20 @@ bool mbm_port_timer_expired(void *inst)
|
||||
mb_event_info_t mb_event;
|
||||
esp_err_t err = ESP_FAIL;
|
||||
|
||||
mb_port_tmr_disable(inst);
|
||||
mb_port_timer_disable(inst);
|
||||
// If timer mode is respond timeout, the master event then turns EV_MASTER_EXECUTE status.
|
||||
if (mb_port_get_cur_tmr_mode(inst) == MB_TMODE_RESPOND_TIMEOUT) {
|
||||
if (mb_port_get_cur_timer_mode(inst) == MB_TMODE_RESPOND_TIMEOUT) {
|
||||
// It is now to check solution.
|
||||
mb_event.event_id = MB_EVENT_TIMEOUT;
|
||||
mb_event.opt_fd = port_obj->pdriver->curr_slave_index;
|
||||
mb_event.opt_fd = port_obj->pdriver->curr_node_index;
|
||||
err = esp_event_isr_post_to(port_obj->pdriver->event_loop_hdl, MB_EVENT_BASE(port_obj->pdriver),
|
||||
(int32_t)MB_EVENT_TIMEOUT, (void *)&mb_event, sizeof(mb_event_info_t*), &task_unblocked);
|
||||
if (err != ESP_OK) {
|
||||
ESP_EARLY_LOGE(TAG, "Timeout event send error: %d", err);
|
||||
}
|
||||
need_poll = task_unblocked;
|
||||
mb_port_evt_set_err_type(inst, EV_ERROR_RESPOND_TIMEOUT);
|
||||
need_poll = mb_port_evt_post(inst, EVENT(EV_ERROR_PROCESS));
|
||||
mb_port_event_set_err_type(inst, EV_ERROR_RESPOND_TIMEOUT);
|
||||
need_poll = mb_port_event_post(inst, EVENT(EV_ERROR_PROCESS));
|
||||
}
|
||||
return need_poll;
|
||||
}
|
||||
@ -226,34 +317,376 @@ mb_uid_info_t *mbm_port_tcp_get_slave_info(mb_port_base_t *inst, uint8_t slave_a
|
||||
{
|
||||
mbm_tcp_port_t *port_obj = __containerof(inst, mbm_tcp_port_t, base);
|
||||
mb_uid_info_t *paddr_info = NULL;
|
||||
mb_slave_info_t *pinfo = mbm_drv_get_slave_info_from_addr(port_obj->pdriver, slave_addr);
|
||||
if (pinfo && (MB_GET_SLAVE_STATE(pinfo) >= exp_state)) {
|
||||
mb_node_info_t *pinfo = mb_drv_get_node_info_from_addr(port_obj->pdriver, slave_addr);
|
||||
if (pinfo && (MB_GET_NODE_STATE(pinfo) >= exp_state)) {
|
||||
paddr_info = &pinfo->addr_info;
|
||||
}
|
||||
return paddr_info;
|
||||
}
|
||||
|
||||
static void mbm_port_tcp_sync_event(void *inst, mb_sync_event_t sync_event)
|
||||
static uint64_t mbm_port_tcp_sync_event(void *inst, mb_sync_event_t sync_event)
|
||||
{
|
||||
switch(sync_event) {
|
||||
case MB_SYNC_EVENT_RECV_OK:
|
||||
mb_port_tmr_disable(inst);
|
||||
mb_port_evt_set_err_type(inst, EV_ERROR_INIT);
|
||||
mb_port_evt_post(inst, EVENT(EV_FRAME_RECEIVED));
|
||||
mb_port_timer_disable(inst);
|
||||
mb_port_event_set_err_type(inst, EV_ERROR_INIT);
|
||||
mb_port_event_post(inst, EVENT(EV_FRAME_RECEIVED));
|
||||
break;
|
||||
|
||||
case MB_SYNC_EVENT_RECV_FAIL:
|
||||
mb_port_tmr_disable(inst);
|
||||
mb_port_evt_set_err_type(inst, EV_ERROR_RECEIVE_DATA);
|
||||
mb_port_evt_post(inst, EVENT(EV_ERROR_PROCESS));
|
||||
mb_port_timer_disable(inst);
|
||||
mb_port_event_set_err_type(inst, EV_ERROR_RECEIVE_DATA);
|
||||
mb_port_event_post(inst, EVENT(EV_ERROR_PROCESS));
|
||||
break;
|
||||
|
||||
case MB_SYNC_EVENT_SEND_OK:
|
||||
mb_port_evt_post(inst, EVENT(EV_FRAME_SENT));
|
||||
mb_port_event_post(inst, EVENT(EV_FRAME_SENT));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return mb_port_get_trans_id(inst);
|
||||
}
|
||||
|
||||
MB_EVENT_HANDLER(mbm_on_ready)
|
||||
{
|
||||
// The driver is registered
|
||||
mb_event_info_t *pevent_info = (mb_event_info_t *)data;
|
||||
ESP_LOGD(TAG, "%s %s: fd: %d", (char *)base, __func__, (int)pevent_info->opt_fd);
|
||||
}
|
||||
|
||||
MB_EVENT_HANDLER(mbm_on_open)
|
||||
{
|
||||
mb_event_info_t *pevent_info = (mb_event_info_t *)data;
|
||||
ESP_LOGD(TAG, "%s %s: fd: %d", (char *)base, __func__, (int)pevent_info->opt_fd);
|
||||
}
|
||||
|
||||
MB_EVENT_HANDLER(mbm_on_resolve)
|
||||
{
|
||||
port_driver_t *pdrv_ctx = MB_GET_DRV_PTR(ctx);
|
||||
mb_event_info_t *pevent_info = (mb_event_info_t *)data;
|
||||
ESP_LOGD(TAG, "%s %s: fd: %d", (char *)base, __func__, (int)pevent_info->opt_fd);
|
||||
|
||||
if (MB_CHECK_FD_RANGE(pevent_info->opt_fd)) {
|
||||
// The mdns is not used in the main app, then can use manually defined IPs
|
||||
int fd = pevent_info->opt_fd;
|
||||
mb_node_info_t *pslave = mb_drv_get_node(pdrv_ctx, fd);
|
||||
if (pslave && (MB_GET_NODE_STATE(pslave) == MB_SOCK_STATE_OPENED)
|
||||
&& FD_ISSET(pslave->index, &pdrv_ctx->open_set)) {
|
||||
mb_status_flags_t status = mb_drv_wait_status_flag(pdrv_ctx, MB_FLAG_DISCONNECTED, 0);
|
||||
if ((status & MB_FLAG_DISCONNECTED)) {
|
||||
ESP_LOGV(TAG, "%p, slave: %d, sock: %d, IP:%s, disconnected.",
|
||||
ctx, (int)pslave->index, (int)pslave->sock_id, pslave->addr_info.ip_addr_str);
|
||||
return;
|
||||
}
|
||||
// The slave IP is defined manually
|
||||
if (port_check_host_addr(pslave->addr_info.node_name_str, NULL)) {
|
||||
pslave->addr_info.ip_addr_str = pslave->addr_info.node_name_str;
|
||||
ESP_LOGD(TAG, "%p, slave: %d, IP address [%s], added to connection list.", ctx, (int)fd, pslave->addr_info.ip_addr_str);
|
||||
MB_SET_NODE_STATE(pslave, MB_SOCK_STATE_RESOLVED);
|
||||
DRIVER_SEND_EVENT(ctx, MB_EVENT_CONNECT, pslave->index);
|
||||
} else {
|
||||
#ifdef MB_MDNS_IS_INCLUDED
|
||||
int ret = port_resolve_mdns_host(pslave->addr_info.node_name_str, (char **)&pslave->addr_info.ip_addr_str);
|
||||
if (ret > 0) {
|
||||
ESP_LOGI(TAG, "%p, slave: %d, resolved with IP:%s.", ctx, (int)fd, pslave->addr_info.ip_addr_str);
|
||||
MB_SET_NODE_STATE(pslave, MB_SOCK_STATE_RESOLVED);
|
||||
DRIVER_SEND_EVENT(ctx, MB_EVENT_CONNECT, pslave->index);
|
||||
} else {
|
||||
// continue resolve while not resolved
|
||||
DRIVER_SEND_EVENT(ctx, MB_EVENT_RESOLVE, pslave->index);
|
||||
}
|
||||
#else
|
||||
ESP_LOGE(TAG, "%p, slave: %d, IP:%s, mdns service is not supported.", ctx, (int)fd, pslave->addr_info.node_name_str);
|
||||
DRIVER_SEND_EVENT(ctx, MB_EVENT_RESOLVE, pslave->index);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
} else if (pevent_info->opt_fd < 0) {
|
||||
// Todo: query for services is removed from this version
|
||||
// #ifdef MB_MDNS_IS_INCLUDED
|
||||
// // If the mDNS feature support is enabled, use it to resolve the slave IP
|
||||
// res = mb_drv_resolve_mdns_service(ctx, "_modbus", "_tcp", pdrv_ctx->addr_type);
|
||||
// ESP_LOGD(TAG, "%p, use mdns to resolve slave: %d, resolved: %d devices.", ctx, (int)pevent_info->opt_fd, res);
|
||||
// #else
|
||||
for (int fd = 0; fd < pdrv_ctx->mb_node_open_count; fd++) {
|
||||
mb_node_info_t *pslave = mb_drv_get_node(pdrv_ctx, fd);
|
||||
if (pslave && (MB_GET_NODE_STATE(pslave) == MB_SOCK_STATE_OPENED)
|
||||
&& FD_ISSET(pslave->index, &pdrv_ctx->open_set)) {
|
||||
DRIVER_SEND_EVENT(ctx, MB_EVENT_RESOLVE, pslave->index);
|
||||
}
|
||||
mb_drv_check_suspend_shutdown(ctx);
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
|
||||
MB_EVENT_HANDLER(mbm_on_connect)
|
||||
{
|
||||
port_driver_t *pdrv_ctx = MB_GET_DRV_PTR(ctx);
|
||||
mb_node_info_t *pnode_info = NULL;
|
||||
mb_event_info_t *pevent_info = (mb_event_info_t *)data;
|
||||
ESP_LOGD(TAG, "%s %s: fd: %d", (char *)base, __func__, (int)pevent_info->opt_fd);
|
||||
err_t err = ERR_CONN;
|
||||
if (MB_CHECK_FD_RANGE(pevent_info->opt_fd)) {
|
||||
pnode_info = mb_drv_get_node(pdrv_ctx, pevent_info->opt_fd);
|
||||
if (pnode_info && (MB_GET_NODE_STATE(pnode_info) < MB_SOCK_STATE_CONNECTED)) {
|
||||
ESP_LOGD(TAG, "%p, connection phase, slave: #%d(%d) [%s].",
|
||||
ctx, (int)pevent_info->opt_fd, (int)pnode_info->sock_id, pnode_info->addr_info.ip_addr_str);
|
||||
if (pnode_info->sock_id != UNDEF_FD) {
|
||||
port_close_connection(pnode_info);
|
||||
}
|
||||
err = port_connect(ctx, pnode_info);
|
||||
switch (err) {
|
||||
case ERR_OK:
|
||||
if (!FD_ISSET(pnode_info->sock_id, &pdrv_ctx->conn_set)) {
|
||||
FD_SET(pnode_info->sock_id, &pdrv_ctx->conn_set);
|
||||
mb_drv_lock(ctx);
|
||||
pdrv_ctx->node_conn_count++;
|
||||
pdrv_ctx->max_conn_sd = (pnode_info->sock_id > pdrv_ctx->max_conn_sd) ? (int)pnode_info->sock_id : pdrv_ctx->max_conn_sd;
|
||||
// Update time stamp for connected slaves
|
||||
pnode_info->send_time = esp_timer_get_time();
|
||||
pnode_info->recv_time = esp_timer_get_time();
|
||||
mb_drv_unlock(ctx);
|
||||
ESP_LOGI(TAG, "%p, slave: #%d, sock:%d, IP: %s, is connected.",
|
||||
ctx, (int)pevent_info->opt_fd, (int)pnode_info->sock_id,
|
||||
pnode_info->addr_info.ip_addr_str);
|
||||
}
|
||||
MB_SET_NODE_STATE(pnode_info, MB_SOCK_STATE_CONNECTED);
|
||||
port_keep_alive(pnode_info);
|
||||
break;
|
||||
case ERR_INPROGRESS:
|
||||
if (FD_ISSET(pnode_info->sock_id, &pdrv_ctx->conn_set)) {
|
||||
FD_CLR(pnode_info->sock_id, &pdrv_ctx->conn_set);
|
||||
ESP_LOGD(TAG, "%p, slave: #%d, sock:%d, IP:%s, connect fail error = %d.",
|
||||
ctx, (int)pevent_info->opt_fd, (int)pnode_info->sock_id,
|
||||
pnode_info->addr_info.ip_addr_str, (int)err);
|
||||
mb_drv_lock(ctx);
|
||||
if (pdrv_ctx->node_conn_count) {
|
||||
pdrv_ctx->node_conn_count--;
|
||||
}
|
||||
mb_drv_unlock(ctx);
|
||||
}
|
||||
MB_SET_NODE_STATE(pnode_info, MB_SOCK_STATE_CONNECTING);
|
||||
vTaskDelay(MB_CONN_TICK_TIMEOUT);
|
||||
// try to connect to slave and check connection again if it is not connected
|
||||
DRIVER_SEND_EVENT(ctx, MB_EVENT_CONNECT, pevent_info->opt_fd);
|
||||
break;
|
||||
case ERR_CONN:
|
||||
ESP_LOGE(TAG, "Modbus connection phase, slave: %d [%s], connection error (%d).",
|
||||
(int)pevent_info->opt_fd, pnode_info->addr_info.ip_addr_str, (int)err);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "Invalid error state, slave: %d [%s], error = %d.",
|
||||
(int)pevent_info->opt_fd, pnode_info->addr_info.ip_addr_str, (int)err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// if the event fd is UNDEF_FD (an event for all slaves),
|
||||
// then perform connection phase for all resolved slaves sending the connection event
|
||||
for (int node = 0; (node < MB_TCP_PORT_MAX_CONN); node++) {
|
||||
pnode_info = mb_drv_get_node(pdrv_ctx, node);
|
||||
if (pnode_info && (MB_GET_NODE_STATE(pnode_info) == MB_SOCK_STATE_RESOLVED)) {
|
||||
if (((pnode_info->sock_id < 0) || !FD_ISSET(pnode_info->sock_id, &pdrv_ctx->conn_set))
|
||||
&& FD_ISSET(node, &pdrv_ctx->open_set)) {
|
||||
DRIVER_SEND_EVENT(ctx, MB_EVENT_CONNECT, pnode_info->index);
|
||||
}
|
||||
}
|
||||
mb_drv_check_suspend_shutdown(ctx);
|
||||
}
|
||||
}
|
||||
ESP_LOGD(TAG, "Opened/connected: %u, %u.",
|
||||
(unsigned)pdrv_ctx->mb_node_open_count, (unsigned)pdrv_ctx->node_conn_count);
|
||||
if (pdrv_ctx->mb_node_open_count == pdrv_ctx->node_conn_count) {
|
||||
if (pdrv_ctx->event_cbs.on_conn_done_cb) {
|
||||
pdrv_ctx->event_cbs.on_conn_done_cb(pdrv_ctx->event_cbs.arg);
|
||||
}
|
||||
ESP_LOGI(TAG, "%p, Connected: %u, %u, start polling.",
|
||||
ctx, (unsigned)pdrv_ctx->mb_node_open_count, (unsigned)pdrv_ctx->node_conn_count);
|
||||
}
|
||||
}
|
||||
|
||||
MB_EVENT_HANDLER(mbm_on_error)
|
||||
{
|
||||
static int curr_fd = 0;
|
||||
port_driver_t *pdrv_ctx = MB_GET_DRV_PTR(ctx);
|
||||
mb_event_info_t *pevent_info = (mb_event_info_t *)data;
|
||||
mb_node_info_t *pnode_info = NULL;
|
||||
if (MB_CHECK_FD_RANGE(pevent_info->opt_fd)) {
|
||||
mb_drv_check_suspend_shutdown(ctx);
|
||||
mb_status_flags_t status = mb_drv_wait_status_flag(pdrv_ctx, MB_FLAG_DISCONNECTED, 0);
|
||||
if ((status & MB_FLAG_DISCONNECTED)) {
|
||||
ESP_LOGE(TAG, "%p, node: %d, is in disconnected state.", ctx, (int)pevent_info->opt_fd);
|
||||
return;
|
||||
}
|
||||
curr_fd = pevent_info->opt_fd;
|
||||
pnode_info = mb_drv_get_next_node_from_set(ctx, &curr_fd, &pdrv_ctx->conn_set);
|
||||
if (pnode_info && (status > 0)) {
|
||||
uint64_t last_read_div_us = esp_timer_get_time() - pnode_info->recv_time;
|
||||
ESP_LOGD(TAG, "%p, slave: %d, sock: %d, IP:%s, check connection, time = %" PRId64 ", rcv_time: %" PRId64,
|
||||
ctx, (int)pnode_info->index, (int)pnode_info->sock_id, pnode_info->addr_info.ip_addr_str,
|
||||
(esp_timer_get_time() / 1000), pnode_info->recv_time / 1000);
|
||||
if (last_read_div_us >= (uint64_t)(MB_RECONNECT_TIME_MS * 1000)) {
|
||||
mb_drv_check_suspend_shutdown(ctx);
|
||||
err_t err = port_check_alive(pnode_info, MB_RECONNECT_TIME_MS);
|
||||
if (err < 0) {
|
||||
ESP_LOGD(TAG, "%p, slave: %d, sock: %d, inactive for %" PRId64 " [ms], reconnect...",
|
||||
ctx, (int)pnode_info->index, (int)pnode_info->sock_id,
|
||||
(last_read_div_us / 1000));
|
||||
MB_SET_NODE_STATE(pnode_info, MB_SOCK_STATE_OPENED);
|
||||
FD_CLR(pnode_info->sock_id, &pdrv_ctx->conn_set);
|
||||
port_close_connection(pnode_info);
|
||||
mb_drv_lock(ctx);
|
||||
pdrv_ctx->node_conn_count--;
|
||||
mb_drv_unlock(ctx);
|
||||
DRIVER_SEND_EVENT(ctx, MB_EVENT_CONNECT, pnode_info->index);
|
||||
} else {
|
||||
curr_fd++;
|
||||
}
|
||||
} else {
|
||||
ESP_LOGD(TAG, "%p, slave: %d, sock: %d, inactive for %" PRId64 " [ms], wait reconnection...",
|
||||
ctx, (int)pnode_info->index, (int)pnode_info->sock_id,
|
||||
(last_read_div_us / 1000));
|
||||
}
|
||||
}
|
||||
} else if (pevent_info->opt_fd < 0) {
|
||||
// send resolve event to all slaves
|
||||
for (int fd = 0; fd < pdrv_ctx->mb_node_open_count; fd++) {
|
||||
mb_drv_check_suspend_shutdown(ctx);
|
||||
mb_node_info_t *pslave = mb_drv_get_node(pdrv_ctx, fd);
|
||||
if (pslave && (MB_GET_NODE_STATE(pslave) == MB_SOCK_STATE_OPENED)
|
||||
&& FD_ISSET(pslave->index, &pdrv_ctx->open_set)) {
|
||||
DRIVER_SEND_EVENT(ctx, MB_EVENT_RESOLVE, pslave->index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MB_EVENT_HANDLER(mbm_on_send_data)
|
||||
{
|
||||
port_driver_t *pdrv_ctx = MB_GET_DRV_PTR(ctx);
|
||||
mb_event_info_t *pevent_info = (mb_event_info_t *)data;
|
||||
ESP_LOGD(TAG, "%s %s: fd: %d", (char *)base, __func__, (int)pevent_info->opt_fd);
|
||||
mb_drv_check_suspend_shutdown(ctx);
|
||||
mb_node_info_t *pinfo = mb_drv_get_node(pdrv_ctx, pevent_info->opt_fd);
|
||||
if (pinfo && !queue_is_empty(pinfo->tx_queue)) {
|
||||
uint8_t tx_buffer[MB_TCP_BUFF_MAX_SIZE] = {0};
|
||||
ESP_LOGD(TAG, "%p, get info: %d, sock_id: %d, queue_state: %d, state: %d.",
|
||||
ctx, (int)pevent_info->opt_fd, (int)pinfo->sock_id,
|
||||
(int)queue_is_empty(pinfo->tx_queue), (int)MB_GET_NODE_STATE(pinfo));
|
||||
size_t sz = queue_pop(pinfo->tx_queue, tx_buffer, sizeof(tx_buffer), NULL);
|
||||
if (MB_GET_NODE_STATE(pinfo) < MB_SOCK_STATE_CONNECTED) {
|
||||
// if slave is not connected, drop data.
|
||||
ESP_LOGE(TAG, "%p, "MB_NODE_FMT(", is invalid, drop send data."),
|
||||
ctx, (int)pinfo->index, (int)pinfo->sock_id, pinfo->addr_info.ip_addr_str);
|
||||
return;
|
||||
}
|
||||
int ret = port_write_poll(pinfo, tx_buffer, sz, MB_TCP_SEND_TIMEOUT_MS);
|
||||
if (ret < 0) {
|
||||
ESP_LOGE(TAG, "%p, "MB_NODE_FMT(", send data failure, err(errno) = %d(%u)."),
|
||||
ctx, (int)pinfo->index, (int)pinfo->sock_id,
|
||||
pinfo->addr_info.ip_addr_str, (int)ret, (unsigned)errno);
|
||||
DRIVER_SEND_EVENT(ctx, MB_EVENT_ERROR, pinfo->index);
|
||||
pinfo->error = ret;
|
||||
} else {
|
||||
ESP_LOGD(TAG, "%p, "MB_NODE_FMT(", send data successful: TID:0x%04x, %d (bytes), errno %d"),
|
||||
ctx, (int)pinfo->index, (int)pinfo->sock_id,
|
||||
pinfo->addr_info.ip_addr_str, (unsigned)pinfo->tid_counter, (int)ret, (unsigned)errno);
|
||||
pinfo->error = 0;
|
||||
// Every successful write increase TID counter
|
||||
if (pinfo->tid_counter < (USHRT_MAX - 1)) {
|
||||
pinfo->tid_counter++;
|
||||
} else {
|
||||
pinfo->tid_counter = (uint16_t)(pinfo->index << 8U);
|
||||
}
|
||||
}
|
||||
pdrv_ctx->event_cbs.mb_sync_event_cb(pdrv_ctx->event_cbs.port_arg, MB_SYNC_EVENT_SEND_OK);
|
||||
mb_drv_lock(ctx);
|
||||
pdrv_ctx->mb_node_curr = pinfo;
|
||||
pdrv_ctx->curr_node_index = pinfo->index;
|
||||
pinfo->send_time = esp_timer_get_time();
|
||||
pinfo->send_counter = (pinfo->send_counter < (USHRT_MAX - 1)) ? (pinfo->send_counter + 1) : 0;
|
||||
mb_drv_unlock(ctx);
|
||||
// Get send buffer from stack
|
||||
ESP_LOG_BUFFER_HEX_LEVEL("SENT", tx_buffer, sz, ESP_LOG_DEBUG);
|
||||
}
|
||||
}
|
||||
|
||||
MB_EVENT_HANDLER(mbm_on_recv_data)
|
||||
{
|
||||
port_driver_t *pdrv_ctx = MB_GET_DRV_PTR(ctx);
|
||||
mb_event_info_t *pevent_info = (mb_event_info_t *)data;
|
||||
ESP_LOGD(TAG, "%s %s: fd: %d", (char *)base, __func__, (int)pevent_info->opt_fd);
|
||||
size_t sz = 0;
|
||||
uint8_t pbuf[MB_TCP_BUFF_MAX_SIZE] = {0};
|
||||
mb_drv_check_suspend_shutdown(ctx);
|
||||
// Get frame from queue, check for correctness, push back correct frame and generate receive condition.
|
||||
// Removes incorrect or expired frames from the queue, leave just correct one then sent sync event
|
||||
mb_node_info_t *pnode_info = mb_drv_get_node(pdrv_ctx, pevent_info->opt_fd);
|
||||
if (pnode_info) {
|
||||
ESP_LOGD(TAG, "%p, slave #%d(%d) [%s], receive data ready.", ctx, (int)pevent_info->opt_fd,
|
||||
(int)pnode_info->sock_id, pnode_info->addr_info.ip_addr_str);
|
||||
while ((sz <= 0) && !queue_is_empty(pnode_info->rx_queue)) {
|
||||
size_t sz = queue_pop(pnode_info->rx_queue, pbuf, MB_TCP_BUFF_MAX_SIZE, NULL);
|
||||
if ((sz > MB_TCP_FUNC) && (sz < sizeof(pbuf))) {
|
||||
uint16_t tid = MB_TCP_MBAP_GET_FIELD(pbuf, MB_TCP_TID);
|
||||
ESP_LOGD(TAG, "%p, packet TID: 0x%04" PRIx16 " received.", ctx, tid);
|
||||
if (tid == (pnode_info->tid_counter - 1)) {
|
||||
queue_push(pnode_info->rx_queue, pbuf, sz, NULL);
|
||||
mb_drv_lock(ctx);
|
||||
pnode_info->recv_time = esp_timer_get_time();
|
||||
mb_drv_unlock(ctx);
|
||||
// send receive event to modbus object
|
||||
pdrv_ctx->event_cbs.mb_sync_event_cb(pdrv_ctx->event_cbs.port_arg, MB_SYNC_EVENT_RECV_OK);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mb_drv_check_suspend_shutdown(ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MB_EVENT_HANDLER(mbm_on_close)
|
||||
{
|
||||
mb_event_info_t *pevent_info = (mb_event_info_t *)data;
|
||||
ESP_LOGD(TAG, "%s %s, fd: %d", (char *)base, __func__, (int)pevent_info->opt_fd);
|
||||
port_driver_t *pdrv_ctx = MB_GET_DRV_PTR(ctx);
|
||||
// if close all sockets event is received
|
||||
if (pevent_info->opt_fd < 0) {
|
||||
(void)mb_drv_clear_status_flag(pdrv_ctx, MB_FLAG_DISCONNECTED);
|
||||
for (int fd = 0; fd < MB_MAX_FDS; fd++) {
|
||||
mb_node_info_t *pslave = mb_drv_get_node(pdrv_ctx, fd);
|
||||
if (pslave && (MB_GET_NODE_STATE(pslave) >= MB_SOCK_STATE_OPENED)
|
||||
&& FD_ISSET(pslave->index, &pdrv_ctx->open_set)) {
|
||||
mb_drv_lock(ctx);
|
||||
// Check connection and unregister slave
|
||||
if ((pslave->sock_id > 0) && (FD_ISSET(pslave->sock_id, &pdrv_ctx->conn_set)) ) {
|
||||
FD_CLR(pslave->sock_id, &pdrv_ctx->conn_set);
|
||||
if (pdrv_ctx->node_conn_count) {
|
||||
pdrv_ctx->node_conn_count--;
|
||||
}
|
||||
}
|
||||
FD_CLR(pslave->index, &pdrv_ctx->open_set);
|
||||
mb_drv_unlock(ctx);
|
||||
// close the socket connection, if active
|
||||
(void)port_close_connection(pslave);
|
||||
// change slave state immediately to release from select
|
||||
MB_SET_NODE_STATE(pslave, MB_SOCK_STATE_READY);
|
||||
}
|
||||
}
|
||||
(void)mb_drv_set_status_flag(pdrv_ctx, MB_FLAG_DISCONNECTED);
|
||||
mb_drv_check_suspend_shutdown(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
MB_EVENT_HANDLER(mbm_on_timeout)
|
||||
{
|
||||
// Socket read/write timeout is triggered
|
||||
mb_event_info_t *pevent_info = (mb_event_info_t *)data;
|
||||
ESP_LOGD(TAG, "%s %s: fd: %d", (char *)base, __func__, (int)pevent_info->opt_fd);
|
||||
// Todo: this event can be used to check network state (kkep empty for now)
|
||||
mb_drv_check_suspend_shutdown(ctx);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -15,6 +15,8 @@
|
||||
#include "mb_common.h"
|
||||
#include "mb_frame.h"
|
||||
|
||||
#include "port_tcp_driver.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@ -26,7 +28,16 @@ typedef struct _uid_info mb_uid_info_t;
|
||||
|
||||
void mbm_port_tcp_set_conn_cb(mb_port_base_t *inst, void *conn_fp, void *arg);
|
||||
mb_uid_info_t *mbm_port_tcp_get_slave_info(mb_port_base_t *inst, uint8_t uid, mb_sock_state_t exp_state);
|
||||
//bool mbm_port_timer_expired(void *inst);
|
||||
|
||||
MB_EVENT_HANDLER(mbm_on_ready);
|
||||
MB_EVENT_HANDLER(mbm_on_open);
|
||||
MB_EVENT_HANDLER(mbm_on_resolve);
|
||||
MB_EVENT_HANDLER(mbm_on_connect);
|
||||
MB_EVENT_HANDLER(mbm_on_send_data);
|
||||
MB_EVENT_HANDLER(mbm_on_recv_data);
|
||||
MB_EVENT_HANDLER(mbm_on_error);
|
||||
MB_EVENT_HANDLER(mbm_on_close);
|
||||
MB_EVENT_HANDLER(mbm_on_timeout);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -3,10 +3,18 @@
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "port_tcp_common.h"
|
||||
#include "port_tcp_slave.h"
|
||||
#include "port_tcp_driver.h"
|
||||
#include "port_tcp_utils.h"
|
||||
|
||||
#include "mb_transaction.h"
|
||||
|
||||
#include "port_common.h" // use common port functions
|
||||
|
||||
#if (CONFIG_FMB_COMM_MODE_TCP_EN)
|
||||
|
||||
@ -15,59 +23,583 @@ typedef struct
|
||||
mb_port_base_t base;
|
||||
// TCP communication properties
|
||||
mb_tcp_opts_t tcp_opts;
|
||||
uint64_t transaction_cnt;
|
||||
uint16_t recv_length;
|
||||
uint64_t send_time_stamp;
|
||||
uint64_t recv_time_stamp;
|
||||
uint32_t flags;
|
||||
TaskHandle_t task_handle;
|
||||
} mb_tcp_port_t;
|
||||
mb_uid_info_t addr_info;
|
||||
uint8_t ptemp_buf[MB_TCP_BUFF_MAX_SIZE];
|
||||
// The driver object for the slave
|
||||
port_driver_t *pdriver;
|
||||
transaction_handle_t transaction;
|
||||
uint16_t trans_count;
|
||||
} mbs_tcp_port_t;
|
||||
|
||||
/* ----------------------- Static variables & functions ----------------------*/
|
||||
static const char *TAG = "mb_port.tcp.slave";
|
||||
|
||||
static uint64_t mbs_port_tcp_sync_event(void *inst, mb_sync_event_t sync_event);
|
||||
|
||||
static esp_err_t mbs_port_tcp_register_handlers(void *ctx)
|
||||
{
|
||||
port_driver_t *pdrv_ctx = MB_GET_DRV_PTR(ctx);
|
||||
esp_err_t ret = ESP_ERR_INVALID_STATE;
|
||||
|
||||
ret = mb_drv_register_handler(pdrv_ctx, MB_EVENT_READY, mbs_on_ready);
|
||||
MB_RETURN_ON_FALSE((ret == ESP_OK), MB_EINVAL, TAG,
|
||||
"%x, mb tcp port event registration failed.", (int)MB_EVENT_READY);
|
||||
ret = mb_drv_register_handler(pdrv_ctx, MB_EVENT_OPEN, mbs_on_open);
|
||||
MB_RETURN_ON_FALSE((ret == ESP_OK), MB_EINVAL, TAG,
|
||||
"%x, mb tcp port event registration failed.", (int)MB_EVENT_OPEN);
|
||||
ret = mb_drv_register_handler(pdrv_ctx, MB_EVENT_CONNECT, mbs_on_connect);
|
||||
MB_RETURN_ON_FALSE((ret == ESP_OK), MB_EINVAL, TAG,
|
||||
"%x, mb tcp port event registration failed.", (int)MB_EVENT_CONNECT);
|
||||
ret = mb_drv_register_handler(pdrv_ctx, MB_EVENT_ERROR, mbs_on_error);
|
||||
MB_RETURN_ON_FALSE((ret == ESP_OK), MB_EINVAL, TAG,
|
||||
"%x, mb tcp port event registration failed.", (int)MB_EVENT_ERROR);
|
||||
ret = mb_drv_register_handler(pdrv_ctx, MB_EVENT_SEND_DATA, mbs_on_send_data);
|
||||
MB_RETURN_ON_FALSE((ret == ESP_OK), MB_EINVAL, TAG,
|
||||
"%x, mb tcp port event registration failed.", (int)MB_EVENT_SEND_DATA);
|
||||
ret = mb_drv_register_handler(pdrv_ctx, MB_EVENT_RECV_DATA, mbs_on_recv_data);
|
||||
MB_RETURN_ON_FALSE((ret == ESP_OK), MB_EINVAL, TAG,
|
||||
"%x, mb tcp port event registration failed.", (int)MB_EVENT_RECV_DATA);
|
||||
ret = mb_drv_register_handler(pdrv_ctx, MB_EVENT_CLOSE, mbs_on_close);
|
||||
MB_RETURN_ON_FALSE((ret == ESP_OK), MB_EINVAL, TAG,
|
||||
"%x, mb tcp port event registration failed.", (int)MB_EVENT_CLOSE);
|
||||
ret = mb_drv_register_handler(pdrv_ctx, MB_EVENT_TIMEOUT, mbs_on_timeout);
|
||||
MB_RETURN_ON_FALSE((ret == ESP_OK), MB_EINVAL, TAG,
|
||||
"%x, mb tcp port event registration failed.", (int)MB_EVENT_TIMEOUT);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t mbs_port_tcp_unregister_handlers(void *ctx)
|
||||
{
|
||||
port_driver_t *pdrv_ctx = MB_GET_DRV_PTR(ctx);
|
||||
esp_err_t ret = ESP_ERR_INVALID_STATE;
|
||||
ESP_LOGD(TAG, "%p, event handler %p, unregister.", pdrv_ctx, pdrv_ctx->event_handler);
|
||||
|
||||
ret = mb_drv_unregister_handler(pdrv_ctx, MB_EVENT_READY);
|
||||
MB_RETURN_ON_FALSE((ret == ESP_OK), MB_EINVAL, TAG,
|
||||
"%x, mb tcp port event registration failed.", (int)MB_EVENT_READY);
|
||||
ret = mb_drv_unregister_handler(pdrv_ctx, MB_EVENT_OPEN);
|
||||
MB_RETURN_ON_FALSE((ret == ESP_OK), MB_EINVAL, TAG,
|
||||
"%x, mb tcp port event registration failed.", (int)MB_EVENT_OPEN);
|
||||
ret = mb_drv_unregister_handler(pdrv_ctx, MB_EVENT_CONNECT);
|
||||
MB_RETURN_ON_FALSE((ret == ESP_OK), MB_EINVAL, TAG,
|
||||
"%x, mb tcp port event registration failed.", (int)MB_EVENT_CONNECT);
|
||||
ret = mb_drv_unregister_handler(pdrv_ctx, MB_EVENT_SEND_DATA);
|
||||
MB_RETURN_ON_FALSE((ret == ESP_OK), MB_EINVAL, TAG,
|
||||
"%x, mb tcp port event registration failed.", (int)MB_EVENT_SEND_DATA);
|
||||
ret = mb_drv_unregister_handler(pdrv_ctx, MB_EVENT_RECV_DATA);
|
||||
MB_RETURN_ON_FALSE((ret == ESP_OK), MB_EINVAL, TAG,
|
||||
"%x, mb tcp port event registration failed.", (int)MB_EVENT_RECV_DATA);
|
||||
ret = mb_drv_unregister_handler(pdrv_ctx, MB_EVENT_CLOSE);
|
||||
MB_RETURN_ON_FALSE((ret == ESP_OK), MB_EINVAL, TAG,
|
||||
"%x, mb tcp port event registration failed.", (int)MB_EVENT_CLOSE);
|
||||
ret = mb_drv_unregister_handler(pdrv_ctx, MB_EVENT_TIMEOUT);
|
||||
MB_RETURN_ON_FALSE((ret == ESP_OK), MB_EINVAL, TAG,
|
||||
"%x, mb tcp port event registration failed.", (int)MB_EVENT_TIMEOUT);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
mb_err_enum_t mbs_port_tcp_create(mb_tcp_opts_t *tcp_opts, mb_port_base_t **port_obj)
|
||||
{
|
||||
mb_tcp_port_t *ptcp = NULL;
|
||||
ptcp = (mb_tcp_port_t*)calloc(1, sizeof(mb_tcp_port_t));
|
||||
MB_RETURN_ON_FALSE((port_obj && tcp_opts), MB_EINVAL, TAG, "mb tcp port invalid arguments.");
|
||||
mbs_tcp_port_t *ptcp = NULL;
|
||||
esp_err_t err = ESP_ERR_INVALID_STATE;
|
||||
ptcp = (mbs_tcp_port_t *)calloc(1, sizeof(mbs_tcp_port_t));
|
||||
MB_RETURN_ON_FALSE((ptcp && port_obj), MB_EILLSTATE, TAG, "mb tcp port creation error.");
|
||||
CRITICAL_SECTION_INIT(ptcp->base.lock);
|
||||
mb_err_enum_t ret = MB_EILLSTATE;
|
||||
|
||||
// Copy object descriptor from parent object (is used for logging)
|
||||
ptcp->base.descr = ((mb_port_base_t *)*port_obj)->descr;
|
||||
ptcp->pdriver = NULL;
|
||||
ptcp->transaction = transaction_init();
|
||||
MB_GOTO_ON_FALSE((ptcp->transaction), MB_EILLSTATE, error,
|
||||
TAG, "mb transaction init failed.");
|
||||
|
||||
ESP_MEM_CHECK(TAG, ptcp->transaction, goto error);
|
||||
|
||||
err = mb_drv_register(&ptcp->pdriver);
|
||||
MB_GOTO_ON_FALSE(((err == ESP_OK) && ptcp->pdriver), MB_EILLSTATE, error,
|
||||
TAG, "mb tcp port driver registration failed, err = (%x).", (int)err);
|
||||
|
||||
err = mbs_port_tcp_register_handlers(ptcp->pdriver);
|
||||
MB_GOTO_ON_FALSE(((err == ESP_OK) && ptcp->pdriver), MB_EILLSTATE, error,
|
||||
TAG, "mb tcp port driver registration failed, err = (%x).", (int)err);
|
||||
|
||||
ptcp->pdriver->parent = ptcp; // just for logging purposes
|
||||
ptcp->tcp_opts = *tcp_opts;
|
||||
ptcp->pdriver->network_iface_ptr = tcp_opts->ip_netif_ptr;
|
||||
ptcp->pdriver->mb_proto = tcp_opts->mode;
|
||||
ptcp->pdriver->uid = tcp_opts->uid;
|
||||
ptcp->pdriver->is_master = false;
|
||||
ptcp->pdriver->event_cbs.mb_sync_event_cb = mbs_port_tcp_sync_event;
|
||||
ptcp->pdriver->event_cbs.port_arg = (void *)ptcp;
|
||||
|
||||
#ifdef MB_MDNS_IS_INCLUDED
|
||||
err = port_start_mdns_service(&ptcp->pdriver->dns_name, false, tcp_opts->uid, ptcp->pdriver->network_iface_ptr);
|
||||
MB_GOTO_ON_FALSE((err == ESP_OK), MB_EILLSTATE, error,
|
||||
TAG, "mb tcp port mdns service init failure.");
|
||||
ESP_LOGD(TAG, "Start mdns for @%p", ptcp);
|
||||
#endif
|
||||
// ptcp->base.cb.tmr_expired = mbs_port_timer_expired;
|
||||
ptcp->base.cb.tx_empty = NULL;
|
||||
ptcp->base.cb.byte_rcvd = NULL;
|
||||
ptcp->base.arg = (void *)ptcp;
|
||||
*port_obj = &(ptcp->base);
|
||||
ESP_LOGD(TAG, "created object @%p", ptcp);
|
||||
return MB_ENOERR;
|
||||
|
||||
error:
|
||||
if (ptcp && ptcp->transaction)
|
||||
{
|
||||
transaction_destroy(ptcp->transaction);
|
||||
}
|
||||
#ifdef MB_MDNS_IS_INCLUDED
|
||||
port_stop_mdns_service(&ptcp->pdriver->dns_name);
|
||||
#endif
|
||||
if (ptcp && ptcp->pdriver)
|
||||
{
|
||||
if (ptcp->pdriver->event_handler)
|
||||
{
|
||||
mbs_port_tcp_unregister_handlers(ptcp->pdriver);
|
||||
ptcp->pdriver->event_handler = NULL;
|
||||
}
|
||||
(void)mb_drv_unregister(ptcp->pdriver);
|
||||
CRITICAL_SECTION_CLOSE(ptcp->base.lock);
|
||||
}
|
||||
free(ptcp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void mbs_port_tcp_delete(mb_port_base_t *inst)
|
||||
{
|
||||
mb_tcp_port_t *port_obj = __containerof(inst, mb_tcp_port_t, base);
|
||||
//vTaskDelete(port_obj->task_handle);
|
||||
mbs_tcp_port_t *port_obj = __containerof(inst, mbs_tcp_port_t, base);
|
||||
if (port_obj && port_obj->transaction)
|
||||
{
|
||||
transaction_destroy(port_obj->transaction);
|
||||
}
|
||||
#ifdef MB_MDNS_IS_INCLUDED
|
||||
port_stop_mdns_service(&port_obj->pdriver->dns_name);
|
||||
#endif
|
||||
if (port_obj && port_obj->pdriver)
|
||||
{
|
||||
if (port_obj->pdriver->event_handler)
|
||||
{
|
||||
mbs_port_tcp_unregister_handlers(port_obj->pdriver);
|
||||
port_obj->pdriver->event_handler = NULL;
|
||||
}
|
||||
(void)mb_drv_unregister(port_obj->pdriver);
|
||||
CRITICAL_SECTION_CLOSE(port_obj->base.lock);
|
||||
}
|
||||
CRITICAL_SECTION_CLOSE(inst->lock);
|
||||
free(port_obj);
|
||||
}
|
||||
|
||||
__attribute__((unused))
|
||||
void mbs_port_tcp_enable(mb_port_base_t *inst)
|
||||
{
|
||||
// mb_tcp_port_t *port_obj = __containerof(inst, mb_tcp_port_t, base);
|
||||
mbs_tcp_port_t *port_obj = __containerof(inst, mbs_tcp_port_t, base);
|
||||
(void)mb_drv_start_task(port_obj->pdriver);
|
||||
DRIVER_SEND_EVENT(port_obj->pdriver, MB_EVENT_READY, UNDEF_FD);
|
||||
}
|
||||
|
||||
__attribute__((unused))
|
||||
void mbs_port_tcp_disable(mb_port_base_t *inst)
|
||||
{
|
||||
// Todo: Temporary unused (needs update)
|
||||
//mb_tcp_port_t *port_obj = __containerof(inst, mb_tcp_port_t, base);
|
||||
mbs_tcp_port_t *port_obj = __containerof(inst, mbs_tcp_port_t, base);
|
||||
// Change the state of all slaves to close
|
||||
DRIVER_SEND_EVENT(port_obj->pdriver, MB_EVENT_CLOSE, UNDEF_FD);
|
||||
(void)mb_drv_wait_status_flag(port_obj->pdriver, MB_FLAG_DISCONNECTED, pdMS_TO_TICKS(MB_RECONNECT_TIME_MS));
|
||||
}
|
||||
|
||||
__attribute__((unused))
|
||||
bool mbs_port_tcp_recv_data(mb_port_base_t *inst, uint8_t **ppframe, uint16_t *plength)
|
||||
{
|
||||
// mb_tcp_port_t *port_obj = __containerof(inst, mb_tcp_port_t, base);
|
||||
return false;
|
||||
mbs_tcp_port_t *port_obj = __containerof(inst, mbs_tcp_port_t, base);
|
||||
port_driver_t *pdrv_ctx = port_obj->pdriver;
|
||||
mb_node_info_t *pnode = NULL;
|
||||
bool status = false;
|
||||
transaction_item_handle_t item;
|
||||
|
||||
if (plength && ppframe && *ppframe)
|
||||
{
|
||||
mb_drv_lock(pdrv_ctx);
|
||||
item = transaction_get_first(port_obj->transaction);
|
||||
if (item && (transaction_item_get_state(item) == ACKNOWLEDGED))
|
||||
{
|
||||
uint16_t tid = 0;
|
||||
int node_id = 0;
|
||||
size_t len = 0;
|
||||
uint8_t *pbuf = transaction_item_get_data(item, &len, &tid, &node_id);
|
||||
pnode = mb_drv_get_node(pdrv_ctx, node_id);
|
||||
if (pbuf && pnode && (MB_GET_NODE_STATE(pnode) >= MB_SOCK_STATE_CONNECTED))
|
||||
{
|
||||
memcpy(*ppframe, pbuf, len);
|
||||
//*ppframe = pbuf;
|
||||
*plength = (uint16_t)len;
|
||||
status = true;
|
||||
ESP_LOGD(TAG, "%p, " MB_NODE_FMT(", get packet TID: 0x%04" PRIx16 ", %p."),
|
||||
port_obj, pnode->index, pnode->sock_id,
|
||||
pnode->addr_info.ip_addr_str, (unsigned)pnode->tid_counter, *ppframe);
|
||||
if (ESP_OK != transaction_item_set_state(item, CONFIRMED)) {
|
||||
ESP_LOGE(TAG, "transaction queue set state fail.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Delete expired frames
|
||||
int frame_cnt = transaction_delete_expired(port_obj->transaction,
|
||||
port_get_timestamp(),
|
||||
(1000 * MB_MASTER_TIMEOUT_MS_RESPOND));
|
||||
if (frame_cnt) {
|
||||
ESP_LOGE(TAG, "Deleted %d expired frames.", frame_cnt);
|
||||
}
|
||||
}
|
||||
mb_drv_unlock(pdrv_ctx);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
bool mbs_port_tcp_send_data(mb_port_base_t *inst, uint8_t *pframe, uint16_t length)
|
||||
{
|
||||
// mb_tcp_port_t *port_obj = __containerof(inst, mb_tcp_port_t, base);
|
||||
mbs_tcp_port_t *port_obj = __containerof(inst, mbs_tcp_port_t, base);
|
||||
|
||||
return false;
|
||||
MB_RETURN_ON_FALSE((pframe && (length > 0)), false, TAG, "incorrect arguments.");
|
||||
bool frame_sent = false;
|
||||
|
||||
uint16_t tid = MB_TCP_MBAP_GET_FIELD(pframe, MB_TCP_TID);
|
||||
port_driver_t *pdrv_ctx = port_obj->pdriver;
|
||||
transaction_item_handle_t item;
|
||||
|
||||
mb_drv_lock(pdrv_ctx);
|
||||
item = transaction_dequeue(port_obj->transaction, CONFIRMED, NULL);
|
||||
if (item) {
|
||||
uint16_t msg_id = 0;
|
||||
int node_id = 0;
|
||||
uint8_t *pbuf = transaction_item_get_data(item, NULL, &msg_id, &node_id);
|
||||
if (pbuf && (tid == msg_id)) {
|
||||
mb_node_info_t *pnode = mb_drv_get_node(pdrv_ctx, node_id);
|
||||
int write_length = mb_drv_write(pdrv_ctx, node_id, pframe, length);
|
||||
if (pnode && write_length) {
|
||||
frame_sent = true;
|
||||
ESP_LOGD(TAG, "%p, node: #%d, socket(#%d)[%s], send packet TID: 0x%04" PRIx16 ":0x%04" PRIx16 ", %p, len: %d, ",
|
||||
pdrv_ctx, pnode->index, pnode->sock_id,
|
||||
pnode->addr_info.node_name_str, (unsigned)tid, (unsigned)msg_id, pframe, length);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "%p, node: #%d, socket(#%d)[%s], mbs_write fail, TID: 0x%04" PRIx16 ":0x%04" PRIx16 ", %p, len: %d, ",
|
||||
pdrv_ctx, pnode->index, pnode->sock_id,
|
||||
pnode->addr_info.node_name_str, (unsigned)tid, (unsigned)msg_id, pframe, length);
|
||||
}
|
||||
if (ESP_OK != transaction_item_set_state(item, REPLIED)) {
|
||||
ESP_LOGE(TAG, "transaction queue set state fail.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "queue can not find the item to send.");
|
||||
}
|
||||
mb_drv_unlock(pdrv_ctx);
|
||||
|
||||
if (!frame_sent)
|
||||
{
|
||||
ESP_LOGE(TAG, "incorrect frame to send.");
|
||||
}
|
||||
return frame_sent;
|
||||
}
|
||||
|
||||
static uint64_t mbs_port_tcp_sync_event(void *inst, mb_sync_event_t sync_event)
|
||||
{
|
||||
switch (sync_event)
|
||||
{
|
||||
case MB_SYNC_EVENT_RECV_OK:
|
||||
mb_port_timer_disable(inst);
|
||||
mb_port_event_set_err_type(inst, EV_ERROR_INIT);
|
||||
mb_port_event_post(inst, EVENT(EV_FRAME_RECEIVED));
|
||||
break;
|
||||
|
||||
case MB_SYNC_EVENT_RECV_FAIL:
|
||||
mb_port_timer_disable(inst);
|
||||
mb_port_event_set_err_type(inst, EV_ERROR_RECEIVE_DATA);
|
||||
mb_port_event_post(inst, EVENT(EV_ERROR_PROCESS));
|
||||
break;
|
||||
|
||||
case MB_SYNC_EVENT_SEND_OK:
|
||||
mb_port_event_post(inst, EVENT(EV_FRAME_SENT));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return mb_port_get_trans_id(inst);
|
||||
}
|
||||
|
||||
MB_EVENT_HANDLER(mbs_on_ready)
|
||||
{
|
||||
// The driver is registered
|
||||
mb_event_info_t *pevent_info = (mb_event_info_t *)data;
|
||||
port_driver_t *pdrv_ctx = MB_GET_DRV_PTR(ctx);
|
||||
mbs_tcp_port_t *port_obj = __containerof(pdrv_ctx->parent, mbs_tcp_port_t, base);
|
||||
ESP_LOGD(TAG, "%s %s: fd: %d", (char *)base, __func__, (int)pevent_info->opt_fd);
|
||||
ESP_LOGD(TAG, "addr_table:%p, addr_type:%d, mode:%d, port:%d", port_obj->tcp_opts.ip_addr_table,
|
||||
(int)port_obj->tcp_opts.addr_type,
|
||||
(int)port_obj->tcp_opts.mode,
|
||||
(int)port_obj->tcp_opts.port);
|
||||
|
||||
int listen_sock = port_bind_addr(port_obj->tcp_opts.ip_addr_table,
|
||||
port_obj->tcp_opts.addr_type,
|
||||
port_obj->tcp_opts.mode,
|
||||
port_obj->tcp_opts.port);
|
||||
if (listen_sock < 0)
|
||||
{
|
||||
mb_drv_check_suspend_shutdown(ctx);
|
||||
ESP_LOGE(TAG, "%s, sock: %d, bind error", (char *)base, listen_sock);
|
||||
mb_drv_lock(pdrv_ctx);
|
||||
if (pdrv_ctx->retry_cnt) pdrv_ctx->retry_cnt--;
|
||||
mb_drv_unlock(pdrv_ctx);
|
||||
if (pdrv_ctx->retry_cnt) {
|
||||
vTaskDelay(TRANSACTION_TICKS);
|
||||
DRIVER_SEND_EVENT(ctx, MB_EVENT_READY, UNDEF_FD);
|
||||
} else {
|
||||
DRIVER_SEND_EVENT(ctx, MB_EVENT_CLOSE, UNDEF_FD);
|
||||
ESP_LOGE(TAG, "%s, stop binding.", (char *)base);
|
||||
// mbs_port_tcp_disable(&port_obj->base);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mb_drv_lock(ctx);
|
||||
pdrv_ctx->listen_sock_fd = listen_sock;
|
||||
mb_drv_unlock(ctx);
|
||||
ESP_LOGI(TAG, "%s %s: fd: %d, bind is done", (char *)base, __func__, (int)pevent_info->opt_fd);
|
||||
}
|
||||
}
|
||||
|
||||
MB_EVENT_HANDLER(mbs_on_open)
|
||||
{
|
||||
mb_event_info_t *pevent_info = (mb_event_info_t *)data;
|
||||
//port_driver_t *pdrv_ctx = MB_GET_DRV_PTR(ctx);
|
||||
ESP_LOGD(TAG, "%s %s: fd: %d", (char *)base, __func__, (int)pevent_info->opt_fd);
|
||||
}
|
||||
|
||||
MB_EVENT_HANDLER(mbs_on_connect)
|
||||
{
|
||||
mb_event_info_t *pevent_info = (mb_event_info_t *)data;
|
||||
port_driver_t *pdrv_ctx = MB_GET_DRV_PTR(ctx);
|
||||
ESP_LOGD(TAG, "%s %s: fd: %d", (char *)base, __func__, (int)pevent_info->opt_fd);
|
||||
mb_node_info_t *pnode = mb_drv_get_node(pdrv_ctx, pevent_info->opt_fd);
|
||||
if (!pnode) {
|
||||
ESP_LOGD(TAG, "%s %s: fd: %d, is closed.", (char *)base, __func__, (int)pevent_info->opt_fd);
|
||||
return;
|
||||
}
|
||||
mb_drv_lock(ctx);
|
||||
MB_SET_NODE_STATE(pnode, MB_SOCK_STATE_CONNECTED);
|
||||
FD_SET(pnode->sock_id, &pdrv_ctx->conn_set);
|
||||
mb_drv_unlock(ctx);
|
||||
}
|
||||
|
||||
MB_EVENT_HANDLER(mbs_on_recv_data)
|
||||
{
|
||||
port_driver_t *pdrv_ctx = MB_GET_DRV_PTR(ctx);
|
||||
mb_event_info_t *pevent_info = (mb_event_info_t *)data;
|
||||
mbs_tcp_port_t *port_obj = (mbs_tcp_port_t *)pdrv_ctx->parent;
|
||||
ESP_LOGD(TAG, "%s %s: fd: %d", (char *)base, __func__, (int)pevent_info->opt_fd);
|
||||
mb_node_info_t *pnode = mb_drv_get_node(pdrv_ctx, pevent_info->opt_fd);
|
||||
transaction_item_handle_t item = NULL;
|
||||
if (pnode)
|
||||
{
|
||||
if (!queue_is_empty(pnode->rx_queue))
|
||||
{
|
||||
ESP_LOGD(TAG, "%p, node #%d(%d) [%s], receive data ready.", ctx, (int)pevent_info->opt_fd,
|
||||
(int)pnode->sock_id, pnode->addr_info.ip_addr_str);
|
||||
frame_entry_t frame_entry;
|
||||
size_t sz = queue_pop(pnode->rx_queue, NULL, MB_BUFFER_SIZE, &frame_entry);
|
||||
if (sz > MB_TCP_FUNC)
|
||||
{
|
||||
uint16_t tid_counter = MB_TCP_MBAP_GET_FIELD(frame_entry.pbuf, MB_TCP_TID);
|
||||
ESP_LOGD(TAG, "%p, " MB_NODE_FMT(", received packet TID: 0x%04" PRIx16 ", %p."),
|
||||
pdrv_ctx, pnode->index, pnode->sock_id,
|
||||
pnode->addr_info.ip_addr_str, (unsigned)tid_counter, frame_entry.pbuf);
|
||||
mb_drv_lock(pdrv_ctx);
|
||||
transaction_message_t msg;
|
||||
msg.buffer = frame_entry.pbuf;
|
||||
msg.len = frame_entry.len;
|
||||
msg.msg_id = frame_entry.tid;
|
||||
msg.node_id = pnode->index;
|
||||
msg.pnode = pnode;
|
||||
item = transaction_enqueue(port_obj->transaction, &msg, port_get_timestamp());
|
||||
pnode->tid_counter = tid_counter; // assign the TID from frame to use it on send
|
||||
mb_drv_unlock(pdrv_ctx);
|
||||
}
|
||||
}
|
||||
mb_drv_lock(pdrv_ctx);
|
||||
item = transaction_get_first(port_obj->transaction);
|
||||
if (item)
|
||||
{
|
||||
if (transaction_item_get_state(item) == QUEUED)
|
||||
{
|
||||
// send receive event to modbus object to get the new data
|
||||
uint16_t msg_id = 0;
|
||||
uint64_t tick = 0;
|
||||
(void)transaction_item_get_data(item, NULL, &msg_id, NULL);
|
||||
tick = port_get_timestamp();
|
||||
pdrv_ctx->event_cbs.mb_sync_event_cb(pdrv_ctx->event_cbs.port_arg, MB_SYNC_EVENT_RECV_OK);
|
||||
transaction_set_tick(port_obj->transaction, msg_id, (transaction_tick_t)tick);
|
||||
if (ESP_OK == transaction_item_set_state(item, ACKNOWLEDGED)) {
|
||||
ESP_LOGD(TAG, "%p, " MB_NODE_FMT(", acknoledged packet TID: 0x%04" PRIx16 "."),
|
||||
pdrv_ctx, pnode->index, pnode->sock_id,
|
||||
pnode->addr_info.ip_addr_str, (unsigned)msg_id);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (transaction_item_get_state(item) != TRANSMITTED) {
|
||||
// Todo: for test removing expired item
|
||||
transaction_delete_expired(port_obj->transaction, port_get_timestamp(), 1000 * 1000);
|
||||
}
|
||||
if (MB_FLAG_TRANSACTION_DONE == mb_drv_wait_status_flag(port_obj->pdriver,
|
||||
MB_FLAG_TRANSACTION_DONE,
|
||||
TRANSACTION_TICKS)) {
|
||||
(void)mb_drv_clear_status_flag(pdrv_ctx, MB_FLAG_TRANSACTION_DONE);
|
||||
}
|
||||
// postpone the packet processing
|
||||
DRIVER_SEND_EVENT(ctx, MB_EVENT_RECV_DATA, pnode->index);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "%p, no queued items found", ctx);
|
||||
}
|
||||
mb_drv_unlock(pdrv_ctx);
|
||||
}
|
||||
mb_drv_check_suspend_shutdown(ctx);
|
||||
}
|
||||
|
||||
MB_EVENT_HANDLER(mbs_on_send_data)
|
||||
{
|
||||
port_driver_t *pdrv_ctx = MB_GET_DRV_PTR(ctx);
|
||||
mb_event_info_t *pevent_info = (mb_event_info_t *)data;
|
||||
mbs_tcp_port_t *port_obj = (mbs_tcp_port_t *)pdrv_ctx->parent;
|
||||
ESP_LOGD(TAG, "%s %s: fd: %d", (char *)base, __func__, (int)pevent_info->opt_fd);
|
||||
mb_node_info_t *pnode = mb_drv_get_node(pdrv_ctx, pevent_info->opt_fd);
|
||||
if (pnode && !queue_is_empty(pnode->tx_queue))
|
||||
{
|
||||
frame_entry_t frame_entry;
|
||||
// pop the frame entry, keep the buffer
|
||||
size_t sz = queue_pop(pnode->tx_queue, NULL, MB_BUFFER_SIZE, &frame_entry);
|
||||
if (!sz || (MB_GET_NODE_STATE(pnode) < MB_SOCK_STATE_CONNECTED)) {
|
||||
ESP_LOGE(TAG, "%p, "MB_NODE_FMT(", is invalid, drop data."),
|
||||
ctx, (int)pnode->index, (int)pnode->sock_id, pnode->addr_info.ip_addr_str);
|
||||
return;
|
||||
}
|
||||
uint16_t tid = MB_TCP_MBAP_GET_FIELD(frame_entry.pbuf, MB_TCP_TID);
|
||||
pnode->error = 0;
|
||||
int ret = port_write_poll(pnode, frame_entry.pbuf, sz, MB_TCP_SEND_TIMEOUT_MS);
|
||||
if (ret < 0)
|
||||
{
|
||||
ESP_LOGE(TAG, "%p, " MB_NODE_FMT(", send data failure, err(errno) = %d(%u)."),
|
||||
ctx, (int)pnode->index, (int)pnode->sock_id,
|
||||
pnode->addr_info.ip_addr_str, (int)ret, (unsigned)errno);
|
||||
DRIVER_SEND_EVENT(ctx, MB_EVENT_ERROR, pnode->index);
|
||||
pnode->error = ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
pnode->error = 0;
|
||||
if (tid != pnode->tid_counter)
|
||||
{
|
||||
ESP_LOGE(TAG, "%p, " MB_NODE_FMT(", send incorrect frame TID:0x%04" PRIx16 "!= 0x%04" PRIx16 ", %d (bytes), errno %d"),
|
||||
ctx, (int)pnode->index, (int)pnode->sock_id,
|
||||
pnode->addr_info.ip_addr_str, pnode->tid_counter, tid, (int)ret, (unsigned)errno);
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGD(TAG, "%p, " MB_NODE_FMT(", send data successful: TID:0x%04" PRIx16 ":0x%04" PRIx16 ", %d (bytes), errno %d"),
|
||||
ctx, (int)pnode->index, (int)pnode->sock_id,
|
||||
pnode->addr_info.ip_addr_str, pnode->tid_counter, tid, (int)ret, (unsigned)errno);
|
||||
}
|
||||
ESP_LOG_BUFFER_HEX_LEVEL("SENT", frame_entry.pbuf, ret, ESP_LOG_DEBUG);
|
||||
}
|
||||
(void)mb_drv_set_status_flag(pdrv_ctx, MB_FLAG_TRANSACTION_DONE);
|
||||
pdrv_ctx->event_cbs.mb_sync_event_cb(pdrv_ctx->event_cbs.port_arg, MB_SYNC_EVENT_SEND_OK);
|
||||
mb_drv_lock(pdrv_ctx);
|
||||
transaction_set_state(port_obj->transaction, tid, TRANSMITTED);
|
||||
if (transaction_delete(port_obj->transaction, tid) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to remove queued TID:0x%04" PRIx16, tid);
|
||||
} else {
|
||||
ESP_LOGD(TAG, "Remove the message TID:0x%04" PRIx16, tid);
|
||||
}
|
||||
free(frame_entry.pbuf);
|
||||
pnode->send_time = esp_timer_get_time();
|
||||
pnode->send_counter = (pnode->send_counter < (USHRT_MAX - 1)) ? (pnode->send_counter + 1) : 0;
|
||||
mb_drv_unlock(pdrv_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
MB_EVENT_HANDLER(mbs_on_error)
|
||||
{
|
||||
port_driver_t *pdrv_ctx = MB_GET_DRV_PTR(ctx);
|
||||
mb_event_info_t *pevent_info = (mb_event_info_t *)data;
|
||||
ESP_LOGD(TAG, "%s %s: fd: %d", (char *)base, __func__, (int)pevent_info->opt_fd);
|
||||
mb_node_info_t *pnode = mb_drv_get_node(pdrv_ctx, pevent_info->opt_fd);
|
||||
if (!pnode) {
|
||||
ESP_LOGD(TAG, "%s %s: fd: %d, is closed.", (char *)base, __func__, (int)pevent_info->opt_fd);
|
||||
return;
|
||||
}
|
||||
port_close_connection(pnode);
|
||||
mb_drv_lock(ctx);
|
||||
// Check connection and unregister slave
|
||||
if ((pnode->sock_id > 0) && (FD_ISSET(pnode->sock_id, &pdrv_ctx->conn_set)))
|
||||
{
|
||||
FD_CLR(pnode->sock_id, &pdrv_ctx->conn_set);
|
||||
if (pdrv_ctx->node_conn_count)
|
||||
{
|
||||
pdrv_ctx->node_conn_count--;
|
||||
}
|
||||
}
|
||||
if (pnode->index && (FD_ISSET(pnode->index, &pdrv_ctx->open_set))) {
|
||||
FD_CLR(pnode->index, &pdrv_ctx->open_set);
|
||||
}
|
||||
mb_drv_unlock(ctx);
|
||||
mb_drv_close(pdrv_ctx, pevent_info->opt_fd);
|
||||
}
|
||||
|
||||
MB_EVENT_HANDLER(mbs_on_close)
|
||||
{
|
||||
mb_event_info_t *pevent_info = (mb_event_info_t *)data;
|
||||
ESP_LOGD(TAG, "%s %s, fd: %d", (char *)base, __func__, (int)pevent_info->opt_fd);
|
||||
port_driver_t *pdrv_ctx = MB_GET_DRV_PTR(ctx);
|
||||
// if close all sockets event is received
|
||||
if (pevent_info->opt_fd < 0)
|
||||
{
|
||||
(void)mb_drv_clear_status_flag(pdrv_ctx, MB_FLAG_DISCONNECTED);
|
||||
for (int fd = 0; fd < MB_MAX_FDS; fd++)
|
||||
{
|
||||
mb_node_info_t *pnode = mb_drv_get_node(pdrv_ctx, fd);
|
||||
if (pnode && (MB_GET_NODE_STATE(pnode) >= MB_SOCK_STATE_OPENED)
|
||||
&& FD_ISSET(pnode->index, &pdrv_ctx->open_set))
|
||||
{
|
||||
mb_drv_lock(ctx);
|
||||
// Check connection and unregister slave
|
||||
if ((pnode->sock_id > 0) && (FD_ISSET(pnode->sock_id, &pdrv_ctx->conn_set)))
|
||||
{
|
||||
FD_CLR(pnode->sock_id, &pdrv_ctx->conn_set);
|
||||
if (pdrv_ctx->node_conn_count) {
|
||||
pdrv_ctx->node_conn_count--;
|
||||
}
|
||||
}
|
||||
FD_CLR(pnode->index, &pdrv_ctx->open_set);
|
||||
mb_drv_unlock(ctx);
|
||||
// close the socket connection, if active
|
||||
(void)port_close_connection(pnode);
|
||||
// change slave state immediately to release from select
|
||||
MB_SET_NODE_STATE(pnode, MB_SOCK_STATE_READY);
|
||||
}
|
||||
}
|
||||
(void)mb_drv_set_status_flag(pdrv_ctx, MB_FLAG_DISCONNECTED);
|
||||
mb_drv_check_suspend_shutdown(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
MB_EVENT_HANDLER(mbs_on_timeout)
|
||||
{
|
||||
// Slave timeout triggered
|
||||
mb_event_info_t *pevent_info = (mb_event_info_t *)data;
|
||||
//port_driver_t *pdrv_ctx = MB_GET_DRV_PTR(ctx);
|
||||
ESP_LOGD(TAG, "%s %s: fd: %d", (char *)base, __func__, (int)pevent_info->opt_fd);
|
||||
// Todo: add network diagnostic here (ping)? Keep empty for now.
|
||||
//mb_drv_check_node_state(pdrv_ctx, UNDEF_FD);
|
||||
mb_drv_check_suspend_shutdown(ctx);
|
||||
}
|
||||
|
||||
#endif
|
56
modbus/mb_ports/tcp/port_tcp_slave.h
Normal file
56
modbus/mb_ports/tcp/port_tcp_slave.h
Normal file
@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
/* ----------------------- Platform includes --------------------------------*/
|
||||
#include "esp_log.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_timer.h"
|
||||
#include "sys/time.h"
|
||||
#include "esp_netif.h"
|
||||
|
||||
/* ----------------------- lwIP includes ------------------------------------*/
|
||||
// #include "lwip/opt.h"
|
||||
// #include "lwip/sys.h"
|
||||
// #include "lwip/err.h"
|
||||
// #include "lwip/sockets.h"
|
||||
// #include "lwip/netdb.h"
|
||||
// #include "net/if.h"
|
||||
|
||||
#include "mb_common.h"
|
||||
#include "mb_frame.h"
|
||||
|
||||
#include "esp_modbus_common.h" // for common types for network options
|
||||
#include "port_tcp_driver.h"
|
||||
#include "sys/queue.h"
|
||||
|
||||
#if (CONFIG_FMB_COMM_MODE_TCP_EN)
|
||||
|
||||
#define TRANSACTION_TICKS pdMS_TO_TICKS(50)
|
||||
|
||||
/**
|
||||
* @brief Modbus slave addr list item for the master
|
||||
*/
|
||||
typedef struct mb_data_entry_s {
|
||||
int node_id;
|
||||
uint64_t token;
|
||||
mb_node_info_t *pnode;
|
||||
frame_entry_t frame;
|
||||
bool pending;
|
||||
STAILQ_ENTRY(mb_data_entry_s) entries;
|
||||
} mb_data_item_t;
|
||||
|
||||
|
||||
MB_EVENT_HANDLER(mbs_on_ready);
|
||||
MB_EVENT_HANDLER(mbs_on_open);
|
||||
MB_EVENT_HANDLER(mbs_on_resolve);
|
||||
MB_EVENT_HANDLER(mbs_on_connect);
|
||||
MB_EVENT_HANDLER(mbs_on_send_data);
|
||||
MB_EVENT_HANDLER(mbs_on_recv_data);
|
||||
MB_EVENT_HANDLER(mbs_on_error);
|
||||
MB_EVENT_HANDLER(mbs_on_close);
|
||||
MB_EVENT_HANDLER(mbs_on_timeout);
|
||||
|
||||
#endif
|
@ -10,6 +10,7 @@
|
||||
#include "port_tcp_master.h"
|
||||
#include "port_tcp_utils.h"
|
||||
#include "port_tcp_driver.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#define TAG "port.utils"
|
||||
|
||||
@ -32,13 +33,14 @@ bool port_check_host_addr(const char *host_str, ip_addr_t *host_addr)
|
||||
// Do name resolution for both protocols
|
||||
hint.ai_family = AF_UNSPEC;
|
||||
hint.ai_flags = AI_ADDRCONFIG; // get IPV6 address if supported, otherwise IPV4
|
||||
hint.ai_flags |= AI_CANONNAME;
|
||||
memset(&target_addr, 0, sizeof(target_addr));
|
||||
|
||||
// convert domain name to IP address
|
||||
// Todo: check EAI_FAIL error when resolve host name
|
||||
int ret = getaddrinfo(host_str, NULL, &hint, &paddr_list);
|
||||
if (ret != 0) {
|
||||
ESP_LOGD(TAG, "Incorrect host name or IP: %s", host_str);
|
||||
ESP_LOGD(TAG, "Incorrect host IP: %s", host_str);
|
||||
return false;
|
||||
}
|
||||
if (paddr_list->ai_family == AF_INET) {
|
||||
@ -56,38 +58,36 @@ bool port_check_host_addr(const char *host_str, ip_addr_t *host_addr)
|
||||
if (host_addr) {
|
||||
*host_addr = target_addr;
|
||||
}
|
||||
ESP_LOGD(TAG, "Check name[IP]: \"%s\"[%s]", paddr_list->ai_canonname, pstr);
|
||||
ESP_LOGD(TAG, "Check name[IP]: \"%s\"[%s]", paddr_list->ai_canonname ? paddr_list->ai_canonname : "UNK", pstr);
|
||||
freeaddrinfo(paddr_list);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool port_close_connection(mb_slave_info_t *pinfo)
|
||||
bool port_close_connection(mb_node_info_t *pinfo)
|
||||
{
|
||||
if (!pinfo) {
|
||||
return false;
|
||||
}
|
||||
if (pinfo->sock_id == -1) {
|
||||
ESP_LOGE(TAG, "Wrong socket info or disconnected socket: %d, skip.", pinfo->sock_id);
|
||||
if (pinfo->sock_id <= 0) {
|
||||
ESP_LOGD(TAG, "wrong socket info or disconnected socket: %d, skip.", pinfo->index);
|
||||
return false;
|
||||
}
|
||||
uint8_t tmp_buff[MB_PDU_SIZE_MAX];
|
||||
|
||||
// Empty tcp buffer before shutdown
|
||||
(void)recv(pinfo->sock_id, &tmp_buff[0], MB_PDU_SIZE_MAX, MSG_DONTWAIT);
|
||||
queue_flush(pinfo->rx_queue);
|
||||
queue_flush(pinfo->tx_queue);
|
||||
|
||||
if (shutdown(pinfo->sock_id, SHUT_RDWR) == -1) {
|
||||
ESP_LOGV(TAG, "Shutdown failed sock %d, errno=%d", pinfo->sock_id, (int)errno);
|
||||
}
|
||||
close(pinfo->sock_id);
|
||||
MB_SET_SLAVE_STATE(pinfo, MB_SOCK_STATE_OPENED);
|
||||
pinfo->sock_id = -1;
|
||||
MB_SET_NODE_STATE(pinfo, MB_SOCK_STATE_OPENED);
|
||||
pinfo->sock_id = UNDEF_FD;
|
||||
return true;
|
||||
}
|
||||
|
||||
mb_slave_info_t *port_get_current_info(void *ctx)
|
||||
{
|
||||
port_driver_t *pdrv_ctx = GET_CONFIG_PTR(ctx);
|
||||
if (!pdrv_ctx->mb_slave_curr_info) {
|
||||
ESP_LOGE(TAG, "Incorrect current slave info.");
|
||||
}
|
||||
return pdrv_ctx->mb_slave_curr_info;
|
||||
}
|
||||
|
||||
// The helper function to get time stamp in microseconds
|
||||
int64_t port_get_timestamp(void)
|
||||
{
|
||||
@ -101,27 +101,6 @@ static void port_ms_to_tv(uint16_t timeout_ms, struct timeval *tv)
|
||||
tv->tv_usec = (timeout_ms - (tv->tv_sec * 1000)) * 1000;
|
||||
}
|
||||
|
||||
void port_check_shutdown(void *ctx)
|
||||
{
|
||||
port_driver_t *pdrv_ctx = GET_CONFIG_PTR(ctx);
|
||||
// First check if the task is not flagged for shutdown
|
||||
if (pdrv_ctx->close_done_sema) {
|
||||
xSemaphoreGive(pdrv_ctx->close_done_sema);
|
||||
vTaskDelete(NULL);
|
||||
ESP_LOGW(TAG, "Destroy task...");
|
||||
}
|
||||
}
|
||||
|
||||
// Function returns time left for response processing according to response timeout
|
||||
int64_t port_get_resp_time_left(mb_slave_info_t *pinfo)
|
||||
{
|
||||
if (!pinfo) {
|
||||
return 0;
|
||||
}
|
||||
int64_t time_stamp = port_get_timestamp() - pinfo->send_time;
|
||||
return (time_stamp > (1000 * MB_MASTER_TIMEOUT_MS_RESPOND)) ? 0 : (MB_MASTER_TIMEOUT_MS_RESPOND - (time_stamp / 1000) - 1);
|
||||
}
|
||||
|
||||
int port_enqueue_packet(QueueHandle_t queue, uint8_t *pbuf, uint16_t len)
|
||||
{
|
||||
frame_entry_t frame_info = {0};
|
||||
@ -143,7 +122,7 @@ int port_enqueue_packet(QueueHandle_t queue, uint8_t *pbuf, uint16_t len)
|
||||
// The packet send fail or the task which is waiting for event is already unblocked
|
||||
return ERR_BUF;
|
||||
} else {
|
||||
ESP_LOGD(TAG, "Enqueue data, length=%d, TID=0x%.4x", frame_info.len, frame_info.tid);
|
||||
ESP_LOGD(TAG, "Enqueue data, length=%d, TID=0x%" PRIx16, frame_info.len, frame_info.tid);
|
||||
return (int)frame_info.len;
|
||||
}
|
||||
} else {
|
||||
@ -162,7 +141,7 @@ int port_dequeue_packet(QueueHandle_t queue, frame_entry_t *pframe_info)
|
||||
if (ret == ESP_OK) {
|
||||
if ((frame_info.pid == 0) && (frame_info.uid < MB_ADDRESS_MAX)) {
|
||||
*pframe_info = frame_info;
|
||||
ESP_LOGD(TAG, "Dequeue data, length=%d, TID=0x%.4x", (int)pframe_info->len, (int)pframe_info->tid);
|
||||
ESP_LOGD(TAG, "Dequeue data, length=%d, TID=0x%" PRIx16, (int)pframe_info->len, (int)pframe_info->tid);
|
||||
return ERR_OK;
|
||||
}
|
||||
} else {
|
||||
@ -172,48 +151,42 @@ int port_dequeue_packet(QueueHandle_t queue, frame_entry_t *pframe_info)
|
||||
return ERR_BUF;
|
||||
}
|
||||
|
||||
static int port_get_buf(void *ctx, mb_slave_info_t *pinfo, uint8_t *pdst_buf, uint16_t len, uint16_t read_tick_ms)
|
||||
static int port_get_buf(mb_node_info_t *pinfo, uint8_t *pdst_buf, uint16_t len, uint16_t read_tick_ms)
|
||||
{
|
||||
int ret = 0;
|
||||
uint8_t *pbuf = pdst_buf;
|
||||
uint16_t bytes_left = len;
|
||||
struct timeval time_val;
|
||||
|
||||
MB_RETURN_ON_FALSE((pinfo && (pinfo->sock_id > -1)), -1, TAG, "Try to read incorrect socket = #%d.", pinfo->sock_id);
|
||||
MB_RETURN_ON_FALSE((pinfo && (pinfo->sock_id > UNDEF_FD)), -1, TAG, "Try to read incorrect socket = #%d.", pinfo->sock_id);
|
||||
|
||||
// Set receive timeout for socket <= slave respond time
|
||||
time_val.tv_sec = read_tick_ms / 1000;
|
||||
time_val.tv_usec = (read_tick_ms % 1000) * 1000;
|
||||
setsockopt(pinfo->sock_id, SOL_SOCKET, SO_RCVTIMEO, &time_val, sizeof(time_val));
|
||||
|
||||
// Receive data from connected client
|
||||
while (bytes_left > 0) {
|
||||
ret = recv(pinfo->sock_id, pbuf, bytes_left, 0);
|
||||
if (ret < 0) {
|
||||
if (errno == EINPROGRESS || errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
// Read timeout occurred, check the timeout and return
|
||||
//return 0;
|
||||
} else if (errno == ENOTCONN) {
|
||||
ESP_LOGE(TAG, "socket(#%d)(%s) connection closed, ret=%d, errno=%d.",
|
||||
pinfo->sock_id, pinfo->addr_info.ip_addr_str, ret, (int)errno);
|
||||
// Socket connection closed
|
||||
return ERR_CONN;
|
||||
} else {
|
||||
// Other error occurred during receiving
|
||||
ESP_LOGE(TAG, "Socket(#%d)(%s) receive error, ret = %d, errno = %d(%s)",
|
||||
pinfo->sock_id, pinfo->addr_info.ip_addr_str, ret, (int)errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
} else if (ret) {
|
||||
pbuf += ret;
|
||||
bytes_left -= ret;
|
||||
// blocking read of data from socket
|
||||
ret = recv(pinfo->sock_id, pbuf, bytes_left, 0);
|
||||
if (ret < 0) {
|
||||
if (errno == EINPROGRESS || errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
// Read timeout occurred, check the timeout and return
|
||||
return 0;
|
||||
} else if (errno == ENOTCONN) {
|
||||
ESP_LOGE(TAG, "socket(#%d)(%s) connection closed, ret=%d, errno=%d.",
|
||||
pinfo->sock_id, pinfo->addr_info.ip_addr_str, ret, (int)errno);
|
||||
// Socket connection closed
|
||||
return ERR_CONN;
|
||||
} else {
|
||||
// Other error occurred during receiving
|
||||
ESP_LOGE(TAG, "Socket(#%d)(%s) receive error, ret = %d, errno = %d(%s)",
|
||||
pinfo->sock_id, pinfo->addr_info.ip_addr_str, ret, (int)errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
port_check_shutdown(ctx);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int port_read_packet(void *ctx, mb_slave_info_t *pinfo)
|
||||
int port_read_packet(mb_node_info_t *pinfo)
|
||||
{
|
||||
uint16_t temp = 0;
|
||||
int ret = 0;
|
||||
@ -223,7 +196,7 @@ int port_read_packet(void *ctx, mb_slave_info_t *pinfo)
|
||||
if (pinfo) {
|
||||
MB_RETURN_ON_FALSE((pinfo->sock_id > 0), -1, TAG, "try to read incorrect socket = #%d.", pinfo->sock_id);
|
||||
// Read packet header
|
||||
ret = port_get_buf(ctx, pinfo, ptemp_buf, MB_TCP_UID, MB_READ_TICK);
|
||||
ret = port_get_buf(pinfo, ptemp_buf, MB_TCP_UID, MB_READ_TICK);
|
||||
if (ret < 0) {
|
||||
pinfo->recv_err = ret;
|
||||
return ret;
|
||||
@ -244,13 +217,13 @@ int port_read_packet(void *ctx, mb_slave_info_t *pinfo)
|
||||
// the number of bytes left to complete the current response.
|
||||
temp = MB_TCP_MBAP_GET_FIELD(ptemp_buf, MB_TCP_LEN);
|
||||
if (temp > MB_TCP_BUFF_MAX_SIZE) {
|
||||
ESP_LOGD("RCV", "Packet length: %d", temp);
|
||||
ESP_LOGD("RCV", "Incorrect packet length: %d", temp);
|
||||
ESP_LOG_BUFFER_HEX_LEVEL(TAG, ptemp_buf, MB_TCP_FUNC, ESP_LOG_DEBUG);
|
||||
pinfo->recv_err = ERR_BUF;
|
||||
temp = MB_TCP_BUFF_MAX_SIZE; // read all remaining data from buffer
|
||||
}
|
||||
|
||||
ret = port_get_buf(ctx, pinfo, &ptemp_buf[MB_TCP_UID], temp, MB_READ_TICK);
|
||||
ret = port_get_buf(pinfo, &ptemp_buf[MB_TCP_UID], temp, MB_READ_TICK);
|
||||
if (ret < 0) {
|
||||
pinfo->recv_err = ret;
|
||||
return ret;
|
||||
@ -278,7 +251,7 @@ int port_read_packet(void *ctx, mb_slave_info_t *pinfo)
|
||||
return -1;
|
||||
}
|
||||
|
||||
err_t port_set_blocking(mb_slave_info_t *pinfo, bool is_blocking)
|
||||
err_t port_set_blocking(mb_node_info_t *pinfo, bool is_blocking)
|
||||
{
|
||||
if (!pinfo) {
|
||||
return ERR_CONN;
|
||||
@ -296,14 +269,14 @@ err_t port_set_blocking(mb_slave_info_t *pinfo, bool is_blocking)
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
void port_keep_alive(mb_slave_info_t *pinfo)
|
||||
void port_keep_alive(mb_node_info_t *pinfo)
|
||||
{
|
||||
int optval = 1;
|
||||
setsockopt(pinfo->sock_id, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
|
||||
}
|
||||
|
||||
// Check connection for timeout helper
|
||||
err_t port_check_alive(mb_slave_info_t *pinfo, uint32_t timeout_ms)
|
||||
err_t port_check_alive(mb_node_info_t *pinfo, uint32_t timeout_ms)
|
||||
{
|
||||
fd_set write_set;
|
||||
fd_set err_set;
|
||||
@ -322,7 +295,7 @@ err_t port_check_alive(mb_slave_info_t *pinfo, uint32_t timeout_ms)
|
||||
if (errno == EINPROGRESS) {
|
||||
err = ERR_INPROGRESS;
|
||||
} else {
|
||||
ESP_LOGV(TAG, MB_SLAVE_FMT(" connection, select write err(errno) = %d(%d)."),
|
||||
ESP_LOGV(TAG, MB_NODE_FMT(" connection, select write err(errno) = %d(%d)."),
|
||||
pinfo->index, pinfo->sock_id, pinfo->addr_info.ip_addr_str, err, (int)errno);
|
||||
err = ERR_CONN;
|
||||
}
|
||||
@ -351,12 +324,12 @@ err_t port_check_alive(mb_slave_info_t *pinfo, uint32_t timeout_ms)
|
||||
}
|
||||
|
||||
// Unblocking connect function
|
||||
err_t port_connect(void *ctx, mb_slave_info_t *pinfo)
|
||||
err_t port_connect(void *ctx, mb_node_info_t *pinfo)
|
||||
{
|
||||
if (!pinfo) {
|
||||
return ERR_CONN;
|
||||
}
|
||||
port_driver_t *pdrv_ctx = GET_CONFIG_PTR(ctx);
|
||||
port_driver_t *pdrv_ctx = MB_GET_DRV_PTR(ctx);
|
||||
err_t err = ERR_OK;
|
||||
char str[HOST_STR_MAX_LEN];
|
||||
char *pstr = NULL;
|
||||
@ -367,8 +340,7 @@ err_t port_connect(void *ctx, mb_slave_info_t *pinfo)
|
||||
|
||||
memset(&hint, 0, sizeof(hint));
|
||||
// Do name resolution for both protocols
|
||||
// hint.ai_family = AF_UNSPEC; Todo: Find a reason why AF_UNSPEC does not work
|
||||
hint.ai_flags = AI_ADDRCONFIG; // get IPV6 address if supported, otherwise IPV4
|
||||
hint.ai_flags = AI_ADDRCONFIG | AI_CANONNAME; // get IPV6 address if supported, otherwise IPV4
|
||||
hint.ai_family = (pinfo->addr_info.addr_type == MB_IPV4) ? AF_INET : AF_INET6;
|
||||
hint.ai_socktype = (pinfo->addr_info.proto == MB_UDP) ? SOCK_DGRAM : SOCK_STREAM;
|
||||
hint.ai_protocol = (pinfo->addr_info.proto == MB_UDP) ? IPPROTO_UDP : IPPROTO_TCP;
|
||||
@ -434,12 +406,12 @@ err_t port_connect(void *ctx, mb_slave_info_t *pinfo)
|
||||
continue;
|
||||
} else if (err != ERR_OK) {
|
||||
// Other error occurred during connection
|
||||
ESP_LOGV(TAG, "%p, "MB_SLAVE_FMT(" unable to connect, error=%d, errno %d (%s)"),
|
||||
ESP_LOGV(TAG, "%p, "MB_NODE_FMT(" unable to connect, error=%d, errno %d (%s)"),
|
||||
ctx, pinfo->index, pinfo->sock_id, str, err, (int)errno, strerror(errno));
|
||||
port_close_connection(pinfo);
|
||||
err = ERR_CONN;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "%p, "MB_SLAVE_FMT(", successfully connected."),
|
||||
ESP_LOGI(TAG, "%p, "MB_NODE_FMT(", successfully connected."),
|
||||
ctx, pinfo->index, pinfo->sock_id, str);
|
||||
continue;
|
||||
}
|
||||
@ -449,25 +421,25 @@ err_t port_connect(void *ctx, mb_slave_info_t *pinfo)
|
||||
return err;
|
||||
}
|
||||
|
||||
int port_write_poll(mb_slave_info_t *pinfo, const uint8_t *pframe, uint16_t frame_len, uint32_t timeout)
|
||||
int port_write_poll(mb_node_info_t *pinfo, const uint8_t *pframe, uint16_t frame_len, uint32_t timeout)
|
||||
{
|
||||
// Check if the socket is alive (writable and SO_ERROR == 0)
|
||||
int res = (int)port_check_alive(pinfo, timeout);
|
||||
if ((res < 0) && (res != ERR_INPROGRESS)) {
|
||||
ESP_LOGE(TAG, MB_SLAVE_FMT(", is not writable, error: %d, errno %d"),
|
||||
ESP_LOGE(TAG, MB_NODE_FMT(", is not writable, error: %d, errno %d"),
|
||||
pinfo->index, pinfo->sock_id, pinfo->addr_info.ip_addr_str, res, (int)errno);
|
||||
return res;
|
||||
}
|
||||
res = send(pinfo->sock_id, pframe, frame_len, TCP_NODELAY);
|
||||
if (res < 0) {
|
||||
ESP_LOGE(TAG, MB_SLAVE_FMT(", send data error: %d, errno %d"),
|
||||
ESP_LOGE(TAG, MB_NODE_FMT(", send data error: %d, errno %d"),
|
||||
pinfo->index, pinfo->sock_id, pinfo->addr_info.ip_addr_str, res, (int)errno);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// Scan IP address according to IPV settings
|
||||
int port_scan_addr_string(char *buffer, mb_uid_info_t *pslave_info)
|
||||
int port_scan_addr_string(char *buffer, mb_uid_info_t *pinfo)
|
||||
{
|
||||
char *phost_str = NULL;
|
||||
unsigned int a[8] = {0};
|
||||
@ -475,7 +447,7 @@ int port_scan_addr_string(char *buffer, mb_uid_info_t *pslave_info)
|
||||
uint16_t index = 0;
|
||||
uint16_t port = 0;
|
||||
|
||||
MB_RETURN_ON_FALSE((buffer && (strlen(buffer) < (HOST_STR_MAX_LEN - 8)) && pslave_info),
|
||||
MB_RETURN_ON_FALSE((buffer && (strlen(buffer) < (HOST_STR_MAX_LEN - 8)) && pinfo),
|
||||
-1, TAG, "check input parameters fail.");
|
||||
|
||||
#if CONFIG_LWIP_IPV6
|
||||
@ -487,13 +459,13 @@ int port_scan_addr_string(char *buffer, mb_uid_info_t *pslave_info)
|
||||
if (-1 == asprintf(&phost_str, IPV6STR, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7])) {
|
||||
abort();
|
||||
}
|
||||
pslave_info->node_name_str = phost_str;
|
||||
pslave_info->ip_addr_str = phost_str;
|
||||
pslave_info->uid = index;
|
||||
pslave_info->fd = index;
|
||||
pslave_info->port = (ret == MB_STR_LEN_IDX_IP6_PORT) ? port : 502;
|
||||
pslave_info->addr_type = MB_IPV6;
|
||||
pslave_info->proto = MB_TCP;
|
||||
pinfo->node_name_str = phost_str;
|
||||
pinfo->ip_addr_str = phost_str;
|
||||
pinfo->uid = index;
|
||||
pinfo->fd = UNDEF_FD;
|
||||
pinfo->port = (ret == MB_STR_LEN_IDX_IP6_PORT) ? port : CONFIG_FMB_TCP_PORT_DEFAULT;
|
||||
pinfo->addr_type = MB_IPV6;
|
||||
pinfo->proto = MB_TCP;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -504,13 +476,13 @@ int port_scan_addr_string(char *buffer, mb_uid_info_t *pslave_info)
|
||||
if (-1 == asprintf(&phost_str, IPV6STR, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7])) {
|
||||
abort();
|
||||
}
|
||||
pslave_info->node_name_str = phost_str;
|
||||
pslave_info->ip_addr_str = phost_str;
|
||||
pslave_info->uid = 0;
|
||||
pslave_info->fd = 0;
|
||||
pslave_info->port = 502;
|
||||
pslave_info->addr_type = MB_IPV6;
|
||||
pslave_info->proto = MB_TCP;
|
||||
pinfo->node_name_str = phost_str;
|
||||
pinfo->ip_addr_str = phost_str;
|
||||
pinfo->uid = 0;
|
||||
pinfo->fd = UNDEF_FD;
|
||||
pinfo->port = CONFIG_FMB_TCP_PORT_DEFAULT;
|
||||
pinfo->addr_type = MB_IPV6;
|
||||
pinfo->proto = MB_TCP;
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
@ -521,13 +493,13 @@ int port_scan_addr_string(char *buffer, mb_uid_info_t *pslave_info)
|
||||
if (-1 == asprintf(&phost_str, IPSTR, a[0], a[1], a[2], a[3])) {
|
||||
abort();
|
||||
}
|
||||
pslave_info->node_name_str = phost_str;
|
||||
pslave_info->ip_addr_str = phost_str;
|
||||
pslave_info->uid = 0;
|
||||
pslave_info->fd = 0;
|
||||
pslave_info->port = 502;
|
||||
pslave_info->addr_type = MB_IPV4;
|
||||
pslave_info->proto = MB_TCP;
|
||||
pinfo->node_name_str = phost_str;
|
||||
pinfo->ip_addr_str = phost_str;
|
||||
pinfo->uid = 0;
|
||||
pinfo->fd = UNDEF_FD;
|
||||
pinfo->port = CONFIG_FMB_TCP_PORT_DEFAULT;
|
||||
pinfo->addr_type = MB_IPV4;
|
||||
pinfo->proto = MB_TCP;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -538,40 +510,42 @@ int port_scan_addr_string(char *buffer, mb_uid_info_t *pslave_info)
|
||||
if (-1 == asprintf(&phost_str, IPSTR, a[0], a[1], a[2], a[3])) {
|
||||
abort();
|
||||
}
|
||||
pslave_info->node_name_str = phost_str;
|
||||
pslave_info->ip_addr_str = phost_str;
|
||||
pslave_info->uid = index;
|
||||
pslave_info->fd = index;
|
||||
pslave_info->port = (ret == MB_STR_LEN_IDX_IP4_PORT) ? port : 502;
|
||||
pslave_info->addr_type = MB_IPV4;
|
||||
pslave_info->proto = MB_TCP;
|
||||
pinfo->node_name_str = phost_str;
|
||||
pinfo->ip_addr_str = phost_str;
|
||||
pinfo->uid = index;
|
||||
pinfo->fd = UNDEF_FD;
|
||||
pinfo->port = (ret == MB_STR_LEN_IDX_IP4_PORT) ? port : CONFIG_FMB_TCP_PORT_DEFAULT;
|
||||
pinfo->addr_type = MB_IPV4;
|
||||
pinfo->proto = MB_TCP;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Configuration format:
|
||||
// "01:mb_slave_tcp_01:1502"
|
||||
// "01:mb_node_tcp_01:502"
|
||||
ret = sscanf(buffer, "%" PRIu16 ";%m[a-z0-9_];%" PRIu16, (uint16_t*)&index, &phost_str, &port);
|
||||
if ((ret == MB_STR_LEN_HOST) || (ret == MB_STR_LEN_IDX_HOST_PORT)) {
|
||||
pslave_info->node_name_str = (phost_str && strlen(phost_str)) ? phost_str : pslave_info->node_name_str;
|
||||
pslave_info->ip_addr_str = (pslave_info->node_name_str) ? pslave_info->node_name_str : pslave_info->ip_addr_str;
|
||||
pslave_info->uid = index;
|
||||
pslave_info->port = (ret == MB_STR_LEN_IDX_HOST_PORT) ? port : 502;
|
||||
pslave_info->addr_type = MB_IPV4;
|
||||
pslave_info->proto = MB_TCP;
|
||||
pinfo->node_name_str = (phost_str && strlen(phost_str)) ? phost_str : pinfo->node_name_str;
|
||||
pinfo->ip_addr_str = (pinfo->node_name_str) ? pinfo->node_name_str : pinfo->ip_addr_str;
|
||||
pinfo->uid = index;
|
||||
pinfo->fd = UNDEF_FD;
|
||||
pinfo->port = (ret == MB_STR_LEN_IDX_HOST_PORT) ? port : CONFIG_FMB_TCP_PORT_DEFAULT;
|
||||
pinfo->addr_type = MB_IPV4;
|
||||
pinfo->proto = MB_TCP;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Configuration format:
|
||||
// "mb_slave_tcp_01"
|
||||
// "mb_node_tcp_01"
|
||||
ret = sscanf(buffer, "%m[a-z0-9_]", &phost_str);
|
||||
if (ret == MB_STR_LEN_HOST) {
|
||||
|
||||
pslave_info->node_name_str = (phost_str && strlen(phost_str)) ? phost_str : pslave_info->node_name_str;
|
||||
pslave_info->ip_addr_str = (pslave_info->node_name_str) ? pslave_info->node_name_str : pslave_info->ip_addr_str;
|
||||
pslave_info->uid = index;
|
||||
pslave_info->port = 502;
|
||||
pslave_info->addr_type = MB_IPV4;
|
||||
pslave_info->proto = MB_TCP;
|
||||
pinfo->node_name_str = (phost_str && strlen(phost_str)) ? phost_str : pinfo->node_name_str;
|
||||
pinfo->ip_addr_str = (pinfo->node_name_str) ? pinfo->node_name_str : pinfo->ip_addr_str;
|
||||
pinfo->uid = index;
|
||||
pinfo->fd = UNDEF_FD;
|
||||
pinfo->port = CONFIG_FMB_TCP_PORT_DEFAULT;
|
||||
pinfo->addr_type = MB_IPV4;
|
||||
pinfo->proto = MB_TCP;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -580,137 +554,357 @@ int port_scan_addr_string(char *buffer, mb_uid_info_t *pslave_info)
|
||||
|
||||
#ifdef MB_MDNS_IS_INCLUDED
|
||||
|
||||
// convert MAC from binary format to string
|
||||
inline char *gen_mac_str(const uint8_t *mac, char *pref, char *mac_str)
|
||||
static int mdns_instance_count = 0;
|
||||
|
||||
inline char *gen_id_str(char *service_name, char *node_id_str)
|
||||
{
|
||||
sprintf(mac_str, "%s%02X%02X%02X%02X%02X%02X", pref, MAC2STR(mac));
|
||||
return mac_str;
|
||||
sprintf(node_id_str, "%s%02X%02X%02X%02X", service_name, MB_ID2STR(MB_DEVICE_ID));
|
||||
return node_id_str;
|
||||
}
|
||||
|
||||
inline char *gen_id_str(char *service_name, char *slave_id_str)
|
||||
// This function has limitation of working with IP6 address only
|
||||
esp_err_t port_start_mdns_service(char **ppdns_name, bool is_master, int uid, void *pnode_netif)
|
||||
{
|
||||
sprintf(slave_id_str, "%s%02X%02X%02X%02X", service_name, MB_ID2STR(MB_DEVICE_ID));
|
||||
return slave_id_str;
|
||||
}
|
||||
|
||||
void port_start_mdns_service(void *ctx)
|
||||
{
|
||||
char temp_str[32] = {0};
|
||||
uint8_t sta_mac[6] = {0};
|
||||
char temp_str[HOST_STR_MAX_LEN] = {0};
|
||||
esp_ip6_addr_t ip6[LWIP_IPV6_NUM_ADDRESSES];
|
||||
int ip6_addrs_count = 0;
|
||||
esp_err_t err = ESP_ERR_INVALID_STATE;
|
||||
err = esp_read_mac(sta_mac, ESP_MAC_WIFI_STA);
|
||||
MB_RETURN_ON_FALSE((err == ESP_OK), ;, TAG, "get STA mac fail, err = %d.", (uint16_t)err);
|
||||
|
||||
char *hostname = gen_mac_str(sta_mac, "mb_master_tcp_", temp_str);
|
||||
MB_RETURN_ON_FALSE((pnode_netif),
|
||||
err, TAG, "Invalid parameters for dns.");
|
||||
|
||||
// initialize mDNS
|
||||
err = mdns_init();
|
||||
MB_RETURN_ON_FALSE((err == ESP_OK), ;, TAG, "mdns init fail, err = %d.", (uint16_t)err);
|
||||
err = mdns_init(); // if the mdns is already initialized on higher layer, just returns
|
||||
MB_RETURN_ON_FALSE(((err == ESP_OK) && pnode_netif), err, TAG, "mdns init fail, err = %d.", (int)err);
|
||||
|
||||
// set mDNS hostname (required if you want to advertise services)
|
||||
err = mdns_hostname_set(hostname);
|
||||
MB_RETURN_ON_FALSE((err == ESP_OK), ;, TAG, "mdns set host name fail, err = %d.", (uint16_t)err);
|
||||
esp_netif_t *pnetif = (esp_netif_t*)pnode_netif;
|
||||
// set mDNS hostname (required if need to advertise services)
|
||||
err = mdns_hostname_get(temp_str);
|
||||
if (err != ESP_OK) {
|
||||
uint8_t mac[6];
|
||||
MB_RETURN_ON_FALSE((esp_netif_get_mac(pnetif, mac) == ESP_OK),
|
||||
err, TAG, "get MAC fail, err = %d.", (int)err);
|
||||
snprintf(temp_str, HOST_STR_MAX_LEN, "%s_%02x%02x%02x", MB_MDNS_INST_NAME(is_master), mac[3], mac[4], mac[5]);
|
||||
err = mdns_hostname_set(temp_str);
|
||||
MB_RETURN_ON_FALSE((err == ESP_OK),
|
||||
err, TAG, "could not set mdns host name, err = %d.", (int)err);
|
||||
ESP_LOGI(TAG, "hostname set to: [%s]", temp_str);
|
||||
err = mdns_instance_name_set(temp_str);
|
||||
MB_RETURN_ON_FALSE((err == ESP_OK),
|
||||
err, TAG, "mdns instance name set fail, err = %d.", (int)err);
|
||||
}
|
||||
// Check if the default mdns name is defined in the configuration
|
||||
if (ppdns_name && *ppdns_name && (strlen(*ppdns_name) >= MB_MDNS_STR_MIN_LENGTH)) {
|
||||
strncpy(temp_str, *ppdns_name, strlen(*ppdns_name));
|
||||
*ppdns_name[strlen(*ppdns_name)] = '\0';
|
||||
} else {
|
||||
if (snprintf(temp_str, sizeof(temp_str), "%s_%02X", MB_MDNS_INST_NAME(is_master), uid) <= 0) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
};
|
||||
}
|
||||
|
||||
// Setup the real assigned dns_name instead of constant string
|
||||
if (ppdns_name) {
|
||||
*ppdns_name = strdup(temp_str);
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "mdns hostname set to: [%s]", hostname);
|
||||
esp_netif_ip_info_t ip_info;
|
||||
mdns_ip_addr_t node_ip;
|
||||
err = esp_netif_get_ip_info(pnetif, &ip_info);
|
||||
MB_RETURN_ON_FALSE((err == ESP_OK),
|
||||
err, TAG, "get IP info fail, err = %d.", (int)err);
|
||||
node_ip.next = NULL;
|
||||
node_ip.addr.type = ESP_IPADDR_TYPE_V4;
|
||||
node_ip.addr.u_addr.ip4 = ip_info.ip;
|
||||
node_ip.addr.u_addr.ip6.addr[1] = 0;
|
||||
node_ip.addr.u_addr.ip6.addr[2] = 0;
|
||||
node_ip.addr.u_addr.ip6.addr[3] = 0;
|
||||
node_ip.addr.u_addr.ip6.zone = 0;
|
||||
err = mdns_delegate_hostname_add(temp_str, &node_ip);
|
||||
MB_RETURN_ON_FALSE((err == ESP_OK), err, TAG, "mdns set delegate name fail, err = %d.", (int)err);
|
||||
|
||||
// set default mDNS instance name
|
||||
err = mdns_instance_name_set("esp32_mb_master_tcp");
|
||||
MB_RETURN_ON_FALSE((err == ESP_OK), ;, TAG, "mdns instance name set fail, err = %d.", (uint16_t)err);
|
||||
#if CONFIG_LWIP_IPV6
|
||||
// If the IP_V6 addresses supported on current interface, delegate them as well.
|
||||
ip6_addrs_count = esp_netif_get_all_ip6(pnetif, ip6);
|
||||
for (int i = 0; i < ip6_addrs_count; ++i) {
|
||||
ESP_LOGI(TAG, "IPv6 address: " IPV6STR, IPV62STR(ip6[i]));
|
||||
node_ip.next = NULL;
|
||||
node_ip.addr.type = ESP_IPADDR_TYPE_V6;
|
||||
node_ip.addr.u_addr.ip6 = ip6[i];
|
||||
err = mdns_delegate_hostname_add(temp_str, &node_ip);
|
||||
MB_RETURN_ON_FALSE((err == ESP_OK), err, TAG, "mdns set delegate name fail, err = %d.", (int)err);
|
||||
}
|
||||
#endif
|
||||
|
||||
// setup dns name for the modbus segment
|
||||
err = mdns_delegate_hostname_add(MB_MDNS_SEGMENT_NAME, &node_ip);
|
||||
MB_RETURN_ON_FALSE((err == ESP_OK), err, TAG, "mdns set segment name fail, err = %d.", (int)err);
|
||||
ESP_LOGI(TAG, "mdns delegate hostname set to: [%s]", temp_str);
|
||||
ESP_LOGI(TAG, "IP: " IPSTR, IP2STR(&ip_info.ip));
|
||||
ESP_LOGI(TAG, "GW: " IPSTR, IP2STR(&ip_info.gw));
|
||||
ESP_LOGI(TAG, "NETMASK: " IPSTR, IP2STR(&ip_info.netmask));
|
||||
|
||||
mdns_instance_count++;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
char *port_get_slave_ip_str(mdns_ip_addr_t *address, mb_addr_type_t addr_type)
|
||||
void port_stop_mdns_service(char **ppdns_name)
|
||||
{
|
||||
mdns_ip_addr_t *a = address;
|
||||
char *slave_ip_str = NULL;
|
||||
|
||||
while (a) {
|
||||
if ((a->addr.type == ESP_IPADDR_TYPE_V6) && (addr_type == MB_IPV6)) {
|
||||
if (-1 == asprintf(&slave_ip_str, IPV6STR, IPV62STR(a->addr.u_addr.ip6))) {
|
||||
abort();
|
||||
}
|
||||
} else if ((a->addr.type == ESP_IPADDR_TYPE_V4) && (addr_type == MB_IPV4)) {
|
||||
if (-1 == asprintf(&slave_ip_str, IPSTR, IP2STR(&(a->addr.u_addr.ip4)))) {
|
||||
abort();
|
||||
}
|
||||
if (ppdns_name && *ppdns_name) {
|
||||
mdns_delegate_hostname_remove(*ppdns_name);
|
||||
free(*ppdns_name);
|
||||
*ppdns_name = NULL;
|
||||
if (mdns_instance_count) {
|
||||
mdns_instance_count--;
|
||||
}
|
||||
if (slave_ip_str) {
|
||||
break;
|
||||
}
|
||||
a = a->next;
|
||||
}
|
||||
return slave_ip_str;
|
||||
if (!mdns_instance_count) {
|
||||
mdns_delegate_hostname_remove(MB_MDNS_SEGMENT_NAME);
|
||||
mdns_service_remove_all();
|
||||
mdns_free();
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t port_resolve_slave(uint8_t short_addr, mdns_result_t *result, char **resolved_ip,
|
||||
mb_addr_type_t addr_type)
|
||||
{
|
||||
if (!short_addr || !result || !resolved_ip) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
mdns_result_t *r = result;
|
||||
int t;
|
||||
char *slave_ip = NULL;
|
||||
char slave_name[22] = {0};
|
||||
// char *port_get_node_ip_str(mdns_ip_addr_t *address, mb_addr_type_t addr_type)
|
||||
// {
|
||||
// mdns_ip_addr_t *a = address;
|
||||
// char *node_ip_str = NULL;
|
||||
|
||||
if (sprintf(slave_name, "mb_slave_tcp_%02X", short_addr) < 0) {
|
||||
ESP_LOGE(TAG, "Fail to create instance name for index: %d", short_addr);
|
||||
abort();
|
||||
}
|
||||
for (; r; r = r->next) {
|
||||
if ((r->ip_protocol == MDNS_IP_PROTOCOL_V4) && (addr_type == MB_IPV6)) {
|
||||
continue;
|
||||
} else if ((r->ip_protocol == MDNS_IP_PROTOCOL_V6) && (addr_type == MB_IPV4)) {
|
||||
continue;
|
||||
}
|
||||
// Check host name for Modbus short address and
|
||||
// append it into slave ip address table
|
||||
if ((strcmp(r->instance_name, slave_name) == 0) && (r->port == CONFIG_FMB_TCP_PORT_DEFAULT)) {
|
||||
printf(" PTR : %s\n", r->instance_name);
|
||||
if (r->txt_count) {
|
||||
printf(" TXT : [%u] ", r->txt_count);
|
||||
for (t = 0; t < r->txt_count; t++) {
|
||||
printf("%s=%s; ", r->txt[t].key, r->txt[t].value ? r->txt[t].value : "NULL");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
slave_ip = port_get_slave_ip_str(r->addr, addr_type);
|
||||
if (slave_ip) {
|
||||
ESP_LOGI(TAG, "Resolved slave %s[%s]:%u", r->hostname, slave_ip, r->port);
|
||||
*resolved_ip = slave_ip;
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
*resolved_ip = NULL;
|
||||
ESP_LOGD(TAG, "Fail to resolve slave: %s", slave_name);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
// while (a) {
|
||||
// if ((a->addr.type == ESP_IPADDR_TYPE_V6) && (addr_type == MB_IPV6)) {
|
||||
// if (-1 == asprintf(&node_ip_str, IPV6STR, IPV62STR(a->addr.u_addr.ip6))) {
|
||||
// abort();
|
||||
// }
|
||||
// } else if ((a->addr.type == ESP_IPADDR_TYPE_V4) && (addr_type == MB_IPV4)) {
|
||||
// if (-1 == asprintf(&node_ip_str, IPSTR, IP2STR(&(a->addr.u_addr.ip4)))) {
|
||||
// abort();
|
||||
// }
|
||||
// }
|
||||
// if (node_ip_str) {
|
||||
// break;
|
||||
// }
|
||||
// a = a->next;
|
||||
// }
|
||||
// return node_ip_str;
|
||||
// }
|
||||
|
||||
// The helper to resolve host name based on service query (not used for now)
|
||||
// esp_err_t port_resolve_slave(uint8_t short_addr, mdns_result_t *result, char **resolved_ip,
|
||||
// mb_addr_type_t addr_type)
|
||||
// {
|
||||
// if (!short_addr || !result || !resolved_ip) {
|
||||
// return ESP_ERR_INVALID_ARG;
|
||||
// }
|
||||
// mdns_result_t *r = result;
|
||||
// int t;
|
||||
// char *node_ip = NULL;
|
||||
// char node_name[22] = {0};
|
||||
|
||||
// if (sprintf(node_name, "mb_node_tcp_%02X", short_addr) < 0) {
|
||||
// ESP_LOGE(TAG, "Fail to create instance name for index: %d", short_addr);
|
||||
// abort();
|
||||
// }
|
||||
// for (; r; r = r->next) {
|
||||
// if ((r->ip_protocol == MDNS_IP_PROTOCOL_V4) && (addr_type == MB_IPV6)) {
|
||||
// continue;
|
||||
// } else if ((r->ip_protocol == MDNS_IP_PROTOCOL_V6) && (addr_type == MB_IPV4)) {
|
||||
// continue;
|
||||
// }
|
||||
// // Check host name for Modbus short address and
|
||||
// // append it into slave ip address table
|
||||
// if ((strcmp(r->instance_name, node_name) == 0) && (r->port == CONFIG_FMB_TCP_PORT_DEFAULT)) {
|
||||
// printf(" PTR : %s\n", r->instance_name);
|
||||
// if (r->txt_count) {
|
||||
// printf(" TXT : [%u] ", r->txt_count);
|
||||
// for (t = 0; t < r->txt_count; t++) {
|
||||
// printf("%s=%s; ", r->txt[t].key, r->txt[t].value ? r->txt[t].value : "NULL");
|
||||
// }
|
||||
// printf("\n");
|
||||
// }
|
||||
// node_ip = port_get_node_ip_str(r->addr, addr_type);
|
||||
// if (node_ip) {
|
||||
// ESP_LOGI(TAG, "Resolved slave %s[%s]:%u", r->hostname, node_ip, r->port);
|
||||
// *resolved_ip = node_ip;
|
||||
// return ESP_OK;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// *resolved_ip = NULL;
|
||||
// ESP_LOGD(TAG, "Fail to resolve slave: %s", node_name);
|
||||
// return ESP_ERR_NOT_FOUND;
|
||||
// }
|
||||
|
||||
int port_resolve_mdns_host(const char *host_name, char **paddr_str)
|
||||
{
|
||||
ESP_LOGW(TAG, "Query A: %s.local", host_name);
|
||||
ESP_LOGD(TAG, "Query A: %s.local", host_name);
|
||||
|
||||
esp_ip4_addr_t addr;
|
||||
addr.addr = 0;
|
||||
esp_ip_addr_t addr;
|
||||
char *pstr = NULL;
|
||||
bzero(&addr, sizeof(esp_ip_addr_t));
|
||||
|
||||
esp_err_t err = mdns_query_a(host_name, MB_MDNS_QUERY_TIME_MS, &addr);
|
||||
// Try to send query to obtain the IPv4 address
|
||||
esp_err_t err = mdns_query_a(host_name, MB_MDNS_QUERY_TIME_MS, &addr.u_addr.ip4);
|
||||
if (err) {
|
||||
if(err == ESP_ERR_NOT_FOUND){
|
||||
ESP_LOGE(TAG, "Host: %s, was not found!", host_name);
|
||||
if (err == ESP_ERR_NOT_FOUND){
|
||||
// Try to resolve using AAAA query
|
||||
err = mdns_query_aaaa(host_name, MB_MDNS_QUERY_TIME_MS, &addr.u_addr.ip6);
|
||||
if (err == ESP_ERR_NOT_FOUND) {
|
||||
ESP_LOGE(TAG, "Host: %s, was not resolved!", host_name);
|
||||
return err;
|
||||
}
|
||||
addr.type = ESP_IPADDR_TYPE_V6;
|
||||
if (asprintf(&pstr, IPV6STR, IPV62STR(addr.u_addr.ip6) == -1)) {
|
||||
abort();
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
} else {
|
||||
addr.type = ESP_IPADDR_TYPE_V4;
|
||||
if (asprintf(&pstr, IPSTR, IP2STR(&addr.u_addr.ip4)) == -1) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
if (asprintf(&pstr, IPSTR, IP2STR(&addr)) == -1) {
|
||||
abort();
|
||||
if (paddr_str) {
|
||||
ESP_LOGD(TAG, "Host: %s, was resolved with IP: %s", host_name, pstr);
|
||||
*paddr_str = pstr;
|
||||
}
|
||||
*paddr_str = pstr;
|
||||
return strlen(pstr);
|
||||
}
|
||||
|
||||
#endif // #ifdef MB_MDNS_IS_INCLUDED
|
||||
|
||||
// Create a listening socket on pbind_ip, with ip address_type, protocol type and port
|
||||
int port_bind_addr(const char *pbind_ip, mb_addr_type_t addr_type, mb_comm_mode_t proto, uint16_t port)
|
||||
{
|
||||
int temp_par, ret;
|
||||
int listen_sock_fd = -1;
|
||||
struct addrinfo hint;
|
||||
struct addrinfo *paddr_list;
|
||||
struct addrinfo *pcur_addr;
|
||||
char* pstr = NULL;
|
||||
|
||||
memset(&hint, 0, sizeof(hint));
|
||||
|
||||
// Bind to IPv6 and/or IPv4, but only in the desired protocol
|
||||
// Todo: Find a reason why AF_UNSPEC does not work for IPv6
|
||||
hint.ai_family = AF_UNSPEC;
|
||||
//hint.ai_family = (addr_type == MB_IPV4) ? AF_INET : AF_INET6;
|
||||
hint.ai_socktype = (proto == MB_UDP) ? SOCK_DGRAM : SOCK_STREAM;
|
||||
// The LWIP has an issue when connection to IPv6 socket
|
||||
hint.ai_protocol = (proto == MB_UDP) ? IPPROTO_UDP : IPPROTO_TCP;
|
||||
hint.ai_flags = AI_NUMERICSERV | AI_PASSIVE | AI_CANONNAME;
|
||||
|
||||
if (asprintf(&pstr, "%u", port) == -1) {
|
||||
abort();
|
||||
}
|
||||
|
||||
ret = getaddrinfo(pbind_ip, pstr, &hint, &paddr_list);
|
||||
free(pstr);
|
||||
|
||||
if ((ret != 0) ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Try the sockaddr until a binding succeeds
|
||||
for (pcur_addr = paddr_list; pcur_addr != NULL; pcur_addr = pcur_addr->ai_next)
|
||||
{
|
||||
listen_sock_fd = (int)socket(pcur_addr->ai_family, pcur_addr->ai_socktype,
|
||||
pcur_addr->ai_protocol);
|
||||
if (listen_sock_fd < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
temp_par = 1;
|
||||
// Allow multi client connections
|
||||
if (setsockopt(listen_sock_fd, SOL_SOCKET, SO_REUSEADDR,
|
||||
(const char*)&temp_par, sizeof(temp_par)) != 0)
|
||||
{
|
||||
close(listen_sock_fd);
|
||||
listen_sock_fd = UNDEF_FD;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bind(listen_sock_fd, (struct sockaddr *)pcur_addr->ai_addr,
|
||||
(socklen_t)pcur_addr->ai_addrlen) != 0 )
|
||||
{
|
||||
close(listen_sock_fd);
|
||||
listen_sock_fd = UNDEF_FD;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Listen only makes sense for TCP
|
||||
if (proto == MB_TCP)
|
||||
{
|
||||
if (listen(listen_sock_fd, MB_TCP_NET_LISTEN_BACKLOG) != 0)
|
||||
{
|
||||
ESP_LOGE(TAG, "Error occurred during listen: errno=%u", (unsigned)errno);
|
||||
close(listen_sock_fd);
|
||||
listen_sock_fd = UNDEF_FD;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Bind was successful
|
||||
pstr = (pcur_addr->ai_canonname == NULL) ? (char *)"\0" : pcur_addr->ai_canonname;
|
||||
ESP_LOGI(TAG, "Socket (#%d), listener %s on port: %u, errno=%u",
|
||||
(int)listen_sock_fd, pstr, (unsigned)port, (unsigned)errno);
|
||||
break;
|
||||
}
|
||||
|
||||
freeaddrinfo(paddr_list);
|
||||
return(listen_sock_fd);
|
||||
}
|
||||
|
||||
int port_accept_connection(int listen_sock_id, mb_uid_info_t *pinfo)
|
||||
{
|
||||
MB_RETURN_ON_FALSE((pinfo), -1, TAG, "Wrong parameter pointer.");
|
||||
MB_RETURN_ON_FALSE((listen_sock_id > 0), -1, TAG, "Incorrect listen socket ID.");
|
||||
|
||||
// Address structure large enough for both IPv4 or IPv6 address
|
||||
struct sockaddr_storage src_addr;
|
||||
char addr_str[128];
|
||||
int sock_id = UNDEF_FD;
|
||||
char *paddr = NULL;
|
||||
socklen_t addr_size = sizeof(struct sockaddr_storage);
|
||||
bzero(&src_addr, sizeof(struct sockaddr_storage));
|
||||
|
||||
// Accept new socket connection if not active
|
||||
sock_id = accept(listen_sock_id, (struct sockaddr *)&src_addr, &addr_size);
|
||||
if (sock_id < 0) {
|
||||
ESP_LOGE(TAG, "Unable to accept connection: errno=%u", (unsigned)errno);
|
||||
close(sock_id);
|
||||
} else {
|
||||
// Get the sender's ip address as string
|
||||
if (src_addr.ss_family == PF_INET) {
|
||||
inet_ntoa_r(((struct sockaddr_in *)&src_addr)->sin_addr.s_addr, addr_str, sizeof(addr_str) - 1);
|
||||
pinfo->port = ntohs(((struct sockaddr_in *)&src_addr)->sin_port);
|
||||
pinfo->addr_type = MB_IPV4;
|
||||
}
|
||||
#if CONFIG_LWIP_IPV6
|
||||
else if (src_addr.ss_family == PF_INET6) {
|
||||
inet6_ntoa_r(((struct sockaddr_in6 *)&src_addr)->sin6_addr, addr_str, sizeof(addr_str) - 1);
|
||||
pinfo->port = ntohs(((struct sockaddr_in6 *)&src_addr)->sin6_port);
|
||||
pinfo->addr_type = MB_IPV6;
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
// Make sure ss_family is valid
|
||||
abort();
|
||||
}
|
||||
ESP_LOGI(TAG, "Socket (#%d), accept client connection from address[port]: %s[%d]", (int)sock_id, addr_str, pinfo->port);
|
||||
paddr = strdup(addr_str);
|
||||
if (paddr) {
|
||||
pinfo->fd = sock_id;
|
||||
pinfo->ip_addr_str = paddr;
|
||||
pinfo->node_name_str = paddr;
|
||||
pinfo->proto = MB_TCP;
|
||||
pinfo->uid = 0;
|
||||
}
|
||||
}
|
||||
return sock_id;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -16,6 +16,7 @@ extern "C" {
|
||||
#include "lwip/sockets.h"
|
||||
#include "lwip/netdb.h"
|
||||
#include "esp_netif.h"
|
||||
#include "net/if.h"
|
||||
|
||||
#include "port_tcp_common.h"
|
||||
|
||||
@ -31,7 +32,13 @@ extern "C" {
|
||||
#include "mdns.h"
|
||||
#endif
|
||||
|
||||
#define HOST_STR_MAX_LEN (64)
|
||||
// Workaround for MDNS_NAME_BUF_LEN being defined in private header
|
||||
#ifndef MDNS_NAME_BUF_LEN
|
||||
#define MDNS_NAME_BUF_LEN 64
|
||||
#endif
|
||||
|
||||
#define HOST_STR_MAX_LEN (MDNS_NAME_BUF_LEN)
|
||||
#define MB_TCP_NET_LISTEN_BACKLOG (SOMAXCONN)
|
||||
|
||||
#if MB_MDNS_IS_INCLUDED
|
||||
|
||||
@ -46,17 +53,17 @@ extern "C" {
|
||||
#define MB_DEVICE_ID (uint32_t)CONFIG_FMB_CONTROLLER_SLAVE_ID
|
||||
#endif
|
||||
|
||||
#define MB_SLAVE_ADDR (CONFIG_MB_SLAVE_ADDR)
|
||||
// #define MB_SLAVE_ADDR (CONFIG_MB_SLAVE_ADDR)
|
||||
|
||||
#endif
|
||||
|
||||
#define MB_MDNS_PORT (502)
|
||||
#define MB_MDNS_PORT (CONFIG_FMB_TCP_PORT_DEFAULT)
|
||||
#define MB_READ_TICK (500)
|
||||
#define MB_MDNS_QUERY_TIME_MS (2000)
|
||||
|
||||
#define MB_STR_LEN_HOST 1 // "mb_slave_tcp_01"
|
||||
#define MB_STR_LEN_IDX_HOST 2 // "12:mb_slave_tcp_01"
|
||||
#define MB_STR_LEN_IDX_HOST_PORT 3 // "01:mb_slave_tcp_01:1502"
|
||||
#define MB_STR_LEN_HOST 1 // "mb_node_tcp_01"
|
||||
#define MB_STR_LEN_IDX_HOST 2 // "12:mb_node_tcp_01"
|
||||
#define MB_STR_LEN_IDX_HOST_PORT 3 // "01:mb_node_tcp_01:1502"
|
||||
#define MB_STR_LEN_IP4_ONLY 4 // "192.168.1.1"
|
||||
#define MB_STR_LEN_IDX_IP4 5 // "1:192.168.1.1"
|
||||
#define MB_STR_LEN_IDX_IP4_PORT 6 // "1:192.168.1.1:502"
|
||||
@ -64,43 +71,58 @@ extern "C" {
|
||||
#define MB_STR_LEN_IDX_IP6 9 // "12:2001:0db8:85a3:0000:0000:8a2e:0370:7334"
|
||||
#define MB_STR_LEN_IDX_IP6_PORT 10 // "12:2001:0db8:85a3:0000:0000:8a2e:0370:7334:502"
|
||||
|
||||
#define MB_MDNS_STR_MIN_LENGTH 10 // "mb_node_01"
|
||||
#define MB_MDNS_SEGMENT_NAME "mb_tcp_segment" // "mb_node_01"
|
||||
|
||||
#define MB_MDNS_INST_NAME(is_master) (__extension__( \
|
||||
{ \
|
||||
((is_master) ? "mb_master_tcp" : "mb_slave_tcp"); \
|
||||
} \
|
||||
))
|
||||
|
||||
typedef struct _frame_queue_entry frame_entry_t;
|
||||
typedef struct _mb_slave_info mb_slave_info_t;
|
||||
typedef struct _mb_node_info mb_node_info_t;
|
||||
typedef enum _addr_type_enum mb_tcp_addr_type_t;
|
||||
|
||||
bool port_check_host_addr(const char *host_str, ip_addr_t* host_addr);
|
||||
mb_slave_info_t* port_get_current_info(void *ctx);
|
||||
mb_node_info_t* port_get_current_info(void *ctx);
|
||||
void port_check_shutdown(void *ctx);
|
||||
int64_t port_get_resp_time_left(mb_slave_info_t* pinfo);
|
||||
int64_t port_get_resp_time_left(mb_node_info_t* pinfo);
|
||||
int port_enqueue_packet(QueueHandle_t queue, uint8_t *pbuf, uint16_t len);
|
||||
int port_dequeue_packet(QueueHandle_t queue, frame_entry_t* pframe_info);
|
||||
int port_read_packet(void *ctx, mb_slave_info_t* pinfo);
|
||||
err_t port_set_blocking(mb_slave_info_t* pinfo, bool is_blocking);
|
||||
void port_keep_alive(mb_slave_info_t* pinfo);
|
||||
err_t port_check_alive(mb_slave_info_t* pinfo, uint32_t timeout_ms);
|
||||
err_t port_connect(void *ctx, mb_slave_info_t* pinfo);
|
||||
bool port_close_connection(mb_slave_info_t* pinfo);
|
||||
int port_write_poll(mb_slave_info_t* pinfo, const uint8_t *pframe, uint16_t frame_len, uint32_t timeout);
|
||||
int port_read_packet(mb_node_info_t* pinfo);
|
||||
err_t port_set_blocking(mb_node_info_t* pinfo, bool is_blocking);
|
||||
void port_keep_alive(mb_node_info_t* pinfo);
|
||||
err_t port_check_alive(mb_node_info_t* pinfo, uint32_t timeout_ms);
|
||||
err_t port_connect(void *ctx, mb_node_info_t* pinfo);
|
||||
bool port_close_connection(mb_node_info_t* pinfo);
|
||||
int port_write_poll(mb_node_info_t* pinfo, const uint8_t *pframe, uint16_t frame_len, uint32_t timeout);
|
||||
int64_t port_get_timestamp(void);
|
||||
|
||||
typedef struct _uid_info mb_uid_info_t;
|
||||
int port_scan_addr_string(char *buffer, mb_uid_info_t *pslave_info);
|
||||
|
||||
int port_scan_addr_string(char *buffer, mb_uid_info_t *pnode_info);
|
||||
|
||||
#if MB_MDNS_IS_INCLUDED
|
||||
|
||||
// convert MAC from binary format to string
|
||||
char *gen_mac_str(const uint8_t *mac, char *pref, char *mac_str);
|
||||
char *gen_id_str(char *service_name, char *slave_id_str);
|
||||
void port_start_mdns_service();
|
||||
// Init mdns service
|
||||
esp_err_t port_start_mdns_service(char **ppdns_name, bool is_master, int uid, void *pnode_netif);
|
||||
void port_stop_mdns_service(char **ppdns_name);
|
||||
|
||||
typedef struct mdns_ip_addr_s mdns_ip_addr_t;
|
||||
typedef struct mdns_result_s mdns_result_t;
|
||||
|
||||
char *port_get_slave_ip_str(mdns_ip_addr_t *address, mb_addr_type_t addr_type);
|
||||
char *port_get_node_ip_str(mdns_ip_addr_t *address, mb_addr_type_t addr_type);
|
||||
esp_err_t port_resolve_slave(uint8_t short_addr, mdns_result_t *result, char **resolved_ip, mb_addr_type_t addr_type);
|
||||
int port_resolve_mdns_host(const char *host_name, char **paddr_str);
|
||||
|
||||
#endif
|
||||
|
||||
// Modbus slave utility functions
|
||||
|
||||
int port_bind_addr(const char *pbind_ip, mb_addr_type_t addr_type, mb_comm_mode_t proto, uint16_t port);
|
||||
int port_accept_connection(int listen_sock_id, mb_uid_info_t *pnode_info);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -25,7 +25,7 @@ typedef struct
|
||||
uint16_t snd_buf_cnt;
|
||||
uint16_t rcv_buf_pos;
|
||||
bool frame_is_broadcast;
|
||||
volatile mb_tmr_mode_enum_t cur_tmr_mode;
|
||||
volatile mb_timer_mode_enum_t cur_timer_mode;
|
||||
} mbm_ascii_trasp_t;
|
||||
|
||||
static mb_err_enum_t mbm_ascii_transp_receive(mb_trans_base_t *inst, uint8_t *rcv_addr_buf, uint8_t **frame_ptr_buf, uint16_t *len_buf);
|
||||
@ -34,7 +34,7 @@ static void mbm_ascii_transp_start(mb_trans_base_t *inst);
|
||||
static void mbm_ascii_transp_stop(mb_trans_base_t *inst);
|
||||
static void mbm_ascii_transp_get_rcv_buf(mb_trans_base_t *inst, uint8_t **frame_ptr_buf);
|
||||
static void mbm_ascii_transp_get_snd_buf(mb_trans_base_t *inst, uint8_t **frame_ptr_buf);
|
||||
static bool mbm_ascii_transp_tmr_expired(void *inst);
|
||||
static bool mbm_ascii_transp_timer_expired(void *inst);
|
||||
static bool mbm_ascii_transp_rq_is_bcast(mb_trans_base_t *inst);
|
||||
|
||||
mb_err_enum_t mbm_ascii_transp_create(mb_serial_opts_t *ser_opts, void **in_out_inst)
|
||||
@ -60,17 +60,17 @@ mb_err_enum_t mbm_ascii_transp_create(mb_serial_opts_t *ser_opts, void **in_out_
|
||||
mb_port_base_t *port_obj = (mb_port_base_t *)*in_out_inst;
|
||||
ret = mb_port_ser_create(ser_opts, &port_obj);
|
||||
MB_GOTO_ON_FALSE((ret == MB_ENOERR), MB_EPORTERR, error, TAG, "serial port creation, err: %d", ret);
|
||||
ret = mb_port_tmr_create(port_obj, (MB_ASCII_TIMEOUT_MS * MB_TIMER_TICS_PER_MS));
|
||||
ret = mb_port_timer_create(port_obj, (MB_ASCII_TIMEOUT_MS * MB_TIMER_TICS_PER_MS));
|
||||
MB_GOTO_ON_FALSE((ret == MB_ENOERR), MB_EPORTERR, error, TAG, "timer port creation, err: %d", ret);
|
||||
// Override default response time if defined
|
||||
if (ser_opts->response_tout_ms) {
|
||||
mb_port_tmr_set_response_time(port_obj, ser_opts->response_tout_ms);
|
||||
mb_port_timer_set_response_time(port_obj, ser_opts->response_tout_ms);
|
||||
}
|
||||
ret = mb_port_evt_create(port_obj);
|
||||
ret = mb_port_event_create(port_obj);
|
||||
MB_GOTO_ON_FALSE((ret == MB_ENOERR), MB_EPORTERR, error, TAG, "event port creation, err: %d", ret);
|
||||
transp->base.port_obj = port_obj;
|
||||
// Set callback function pointer for the timer
|
||||
port_obj->cb.tmr_expired = mbm_ascii_transp_tmr_expired;
|
||||
port_obj->cb.tmr_expired = mbm_ascii_transp_timer_expired;
|
||||
port_obj->cb.tx_empty = NULL;
|
||||
port_obj->cb.byte_rcvd = NULL;
|
||||
port_obj->arg = (void *)transp;
|
||||
@ -97,8 +97,8 @@ error:
|
||||
bool mbm_ascii_transp_delete(mb_trans_base_t *inst)
|
||||
{
|
||||
mbm_ascii_trasp_t *trans = __containerof(inst, mbm_ascii_trasp_t, base);
|
||||
mb_port_tmr_delete(trans->base.port_obj);
|
||||
mb_port_evt_delete(trans->base.port_obj);
|
||||
mb_port_timer_delete(trans->base.port_obj);
|
||||
mb_port_event_delete(trans->base.port_obj);
|
||||
mb_port_ser_delete(trans->base.port_obj);
|
||||
free((void *)trans->pascii_puf);
|
||||
CRITICAL_SECTION_CLOSE(inst->lock);
|
||||
@ -112,18 +112,18 @@ static void mbm_ascii_transp_start(mb_trans_base_t *inst)
|
||||
|
||||
CRITICAL_SECTION(inst->lock) {
|
||||
mb_port_ser_enable(inst->port_obj);
|
||||
mb_port_tmr_enable(inst->port_obj);
|
||||
mb_port_timer_enable(inst->port_obj);
|
||||
};
|
||||
|
||||
/* No special startup required for ASCII. */
|
||||
(void)mb_port_evt_post(transp->base.port_obj, EVENT(EV_READY));
|
||||
(void)mb_port_event_post(transp->base.port_obj, EVENT(EV_READY));
|
||||
}
|
||||
|
||||
static void mbm_ascii_transp_stop(mb_trans_base_t *inst)
|
||||
{
|
||||
CRITICAL_SECTION(inst->lock) {
|
||||
mb_port_ser_disable(inst->port_obj);
|
||||
mb_port_tmr_disable(inst->port_obj);
|
||||
mb_port_timer_disable(inst->port_obj);
|
||||
};
|
||||
}
|
||||
|
||||
@ -199,9 +199,9 @@ static mb_err_enum_t mbm_ascii_transp_send(mb_trans_base_t *inst, uint8_t slv_ad
|
||||
// If the frame is broadcast, master will enable timer of convert delay,
|
||||
// else master will enable timer of respond timeout. */
|
||||
if (transp->frame_is_broadcast) {
|
||||
mb_port_tmr_convert_delay_enable(transp->base.port_obj);
|
||||
mb_port_timer_convert_delay_enable(transp->base.port_obj);
|
||||
} else {
|
||||
mb_port_tmr_respond_timeout_enable(transp->base.port_obj);
|
||||
mb_port_timer_respond_timeout_enable(transp->base.port_obj);
|
||||
}
|
||||
} else {
|
||||
status = MB_EIO;
|
||||
@ -227,35 +227,35 @@ static bool mbm_ascii_transp_snd_fsm(mb_trans_base_t *inst)
|
||||
}
|
||||
|
||||
// The timer expired function
|
||||
static bool mbm_ascii_transp_tmr_expired(void *inst)
|
||||
static bool mbm_ascii_transp_timer_expired(void *inst)
|
||||
{
|
||||
mbm_ascii_trasp_t *transp = __containerof(inst, mbm_ascii_trasp_t, base);
|
||||
|
||||
bool need_poll = false;
|
||||
mb_tmr_mode_enum_t timer_mode = mb_port_get_cur_tmr_mode(transp->base.port_obj);
|
||||
mb_timer_mode_enum_t timer_mode = mb_port_get_cur_timer_mode(transp->base.port_obj);
|
||||
|
||||
mb_port_tmr_disable(transp->base.port_obj);
|
||||
mb_port_timer_disable(transp->base.port_obj);
|
||||
|
||||
switch(timer_mode) {
|
||||
case MB_TMODE_T35:
|
||||
need_poll = mb_port_evt_post(transp->base.port_obj, EVENT(EV_READY));
|
||||
need_poll = mb_port_event_post(transp->base.port_obj, EVENT(EV_READY));
|
||||
ESP_EARLY_LOGD(TAG, "%p:EV_READY", transp->base.descr.parent);
|
||||
break;
|
||||
|
||||
case MB_TMODE_RESPOND_TIMEOUT:
|
||||
mb_port_evt_set_err_type(transp->base.port_obj, EV_ERROR_RESPOND_TIMEOUT);
|
||||
need_poll = mb_port_evt_post(transp->base.port_obj, EVENT(EV_ERROR_PROCESS));
|
||||
mb_port_event_set_err_type(transp->base.port_obj, EV_ERROR_RESPOND_TIMEOUT);
|
||||
need_poll = mb_port_event_post(transp->base.port_obj, EVENT(EV_ERROR_PROCESS));
|
||||
ESP_EARLY_LOGD(TAG, "%p:EV_ERROR_RESPOND_TIMEOUT", transp->base.descr.parent);
|
||||
break;
|
||||
|
||||
case MB_TMODE_CONVERT_DELAY:
|
||||
/* If timer mode is convert delay, the master event then turns EV_MASTER_EXECUTE status. */
|
||||
need_poll = mb_port_evt_post(transp->base.port_obj, EVENT(EV_EXECUTE));
|
||||
need_poll = mb_port_event_post(transp->base.port_obj, EVENT(EV_EXECUTE));
|
||||
ESP_EARLY_LOGD(TAG, "%p:MB_TMODE_CONVERT_DELAY", transp->base.descr.parent);
|
||||
break;
|
||||
|
||||
default:
|
||||
need_poll = mb_port_evt_post(transp->base.port_obj, EVENT(EV_READY));
|
||||
need_poll = mb_port_event_post(transp->base.port_obj, EVENT(EV_READY));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -278,7 +278,6 @@ static void mbm_ascii_transp_get_snd_buf(mb_trans_base_t *inst, uint8_t **frame_
|
||||
}
|
||||
}
|
||||
|
||||
// void mb_ascii_set_cur_tmr_mode(mb_ascii_tr_struct* inst, mb_tmr_mode_enum_t tmr_mode);
|
||||
static bool mbm_ascii_transp_rq_is_bcast(mb_trans_base_t *inst)
|
||||
{
|
||||
mbm_ascii_trasp_t *transp = __containerof(inst, mbm_ascii_trasp_t, base);
|
||||
|
@ -25,7 +25,7 @@ typedef struct
|
||||
uint8_t *snd_buf_cur;
|
||||
uint16_t snd_buf_cnt;
|
||||
uint16_t rcv_buf_pos;
|
||||
volatile mb_tmr_mode_enum_t cur_tmr_mode;
|
||||
volatile mb_timer_mode_enum_t cur_timer_mode;
|
||||
} mbs_ascii_trasp_t;
|
||||
|
||||
mb_err_enum_t mbs_ascii_transp_create(mb_serial_opts_t *ser_opts, void **in_out_inst);
|
||||
@ -35,7 +35,7 @@ static mb_err_enum_t mbs_ascii_transp_receive(mb_trans_base_t *inst, uint8_t *rc
|
||||
static mb_err_enum_t mbs_ascii_transp_send(mb_trans_base_t *inst, uint8_t slv_addr, const uint8_t *frame_ptr, uint16_t len);
|
||||
static bool mbs_ascii_transp_rcv_fsm(mb_trans_base_t *inst);
|
||||
static bool mbs_ascii_transp_snd_fsm(mb_trans_base_t *inst);
|
||||
static bool mbs_ascii_transp_tmr_expired(void *inst);
|
||||
static bool mbs_ascii_transp_timer_expired(void *inst);
|
||||
void mbs_ascii_transp_get_rcv_buf(mb_trans_base_t *inst, uint8_t **frame_ptr_buf);
|
||||
static void mbs_ascii_transp_get_snd_buf(mb_trans_base_t *inst, uint8_t **frame_ptr_buf);
|
||||
|
||||
@ -62,14 +62,14 @@ mb_err_enum_t mbs_ascii_transp_create(mb_serial_opts_t *ser_opts, void **in_out_
|
||||
mb_port_base_t *port_obj = (mb_port_base_t *)*in_out_inst;
|
||||
ret = mb_port_ser_create(ser_opts, &port_obj);
|
||||
MB_GOTO_ON_FALSE((ret == MB_ENOERR), MB_EPORTERR, error, TAG, "serial port creation, err: %d", ret);
|
||||
ret = mb_port_tmr_create(port_obj, (MB_ASCII_TIMEOUT_MS * MB_TIMER_TICS_PER_MS));
|
||||
ret = mb_port_timer_create(port_obj, (MB_ASCII_TIMEOUT_MS * MB_TIMER_TICS_PER_MS));
|
||||
MB_GOTO_ON_FALSE((ret == MB_ENOERR), MB_EPORTERR, error, TAG, "timer port creation, err: %d", ret);
|
||||
ret = mb_port_evt_create(port_obj);
|
||||
ret = mb_port_event_create(port_obj);
|
||||
MB_GOTO_ON_FALSE((ret == MB_ENOERR), MB_EPORTERR, error, TAG, "event port creation, err: %d", ret);
|
||||
transp->base.port_obj = port_obj;
|
||||
transp->rcv_buf = (uint8_t *)&transp->pdu_buf[0];
|
||||
// Set callback function pointer for the timer
|
||||
port_obj->cb.tmr_expired = mbs_ascii_transp_tmr_expired;
|
||||
port_obj->cb.tmr_expired = mbs_ascii_transp_timer_expired;
|
||||
port_obj->cb.tx_empty = NULL;
|
||||
port_obj->cb.byte_rcvd = NULL;
|
||||
port_obj->arg = (void *)transp;
|
||||
@ -94,8 +94,8 @@ error:
|
||||
bool mbs_ascii_transp_delete(mb_trans_base_t *inst)
|
||||
{
|
||||
mbs_ascii_trasp_t *transp = __containerof(inst, mbs_ascii_trasp_t, base);
|
||||
mb_port_tmr_delete(transp->base.port_obj);
|
||||
mb_port_evt_delete(transp->base.port_obj);
|
||||
mb_port_timer_delete(transp->base.port_obj);
|
||||
mb_port_event_delete(transp->base.port_obj);
|
||||
mb_port_ser_delete(transp->base.port_obj);
|
||||
free(transp->pascii_puf);
|
||||
CRITICAL_SECTION_CLOSE(inst->lock);
|
||||
@ -108,18 +108,18 @@ static void mbs_ascii_transp_start(mb_trans_base_t *inst)
|
||||
mbs_ascii_trasp_t *transp = __containerof(inst, mbs_ascii_trasp_t, base);
|
||||
CRITICAL_SECTION(inst->lock) {
|
||||
mb_port_ser_enable(inst->port_obj);
|
||||
mb_port_tmr_enable(inst->port_obj);
|
||||
mb_port_timer_enable(inst->port_obj);
|
||||
};
|
||||
|
||||
/* No special startup required for ASCII. */
|
||||
(void)mb_port_evt_post(transp->base.port_obj, EVENT(EV_READY));
|
||||
(void)mb_port_event_post(transp->base.port_obj, EVENT(EV_READY));
|
||||
}
|
||||
|
||||
static void mbs_ascii_transp_stop(mb_trans_base_t *inst)
|
||||
{
|
||||
CRITICAL_SECTION(inst->lock) {
|
||||
mb_port_ser_disable(inst->port_obj);
|
||||
mb_port_tmr_disable(inst->port_obj);
|
||||
mb_port_timer_disable(inst->port_obj);
|
||||
};
|
||||
}
|
||||
|
||||
@ -208,11 +208,11 @@ static bool mbs_ascii_transp_snd_fsm(mb_trans_base_t *inst)
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool mbs_ascii_transp_tmr_expired(void *inst)
|
||||
static bool mbs_ascii_transp_timer_expired(void *inst)
|
||||
{
|
||||
mbs_ascii_trasp_t *transp = __containerof(inst, mbs_ascii_trasp_t, base);
|
||||
|
||||
mb_port_tmr_disable(transp->base.port_obj);
|
||||
mb_port_timer_disable(transp->base.port_obj);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ typedef struct
|
||||
uint16_t snd_buf_cnt;
|
||||
uint16_t rcv_buf_pos;
|
||||
bool frame_is_broadcast;
|
||||
volatile mb_tmr_mode_enum_t cur_tmr_mode;
|
||||
volatile mb_timer_mode_enum_t cur_timer_mode;
|
||||
mb_rtu_state_enum_t state;
|
||||
} mbm_rtu_transp_t;
|
||||
|
||||
@ -35,7 +35,7 @@ static mb_err_enum_t mbm_rtu_transp_receive(mb_trans_base_t *inst, uint8_t *rcv_
|
||||
static mb_err_enum_t mbm_rtu_transp_send(mb_trans_base_t *inst, uint8_t slv_addr, const uint8_t *frame_ptr, uint16_t len);
|
||||
static bool mbm_rtu_transp_rcv_fsm(mb_trans_base_t *inst);
|
||||
static bool mbm_rtu_transp_snd_fsm(mb_trans_base_t *inst);
|
||||
static bool mbm_rtu_transp_tmr_35_expired(void *inst);
|
||||
static bool mbm_rtu_transp_timer_expired(void *inst);
|
||||
static void mbm_rtu_transp_get_snd_buf(mb_trans_base_t *inst, uint8_t **frame_ptr_buf);
|
||||
static void mbm_rtu_transp_get_rcv_buf(mb_trans_base_t *inst, uint8_t **frame_ptr_buf);
|
||||
// static uint16_t mbm_rtu_transp_get_snd_len(mb_trans_base_t *inst);
|
||||
@ -66,17 +66,17 @@ mb_err_enum_t mbm_rtu_transp_create(mb_serial_opts_t *ser_opts, void **in_out_in
|
||||
mb_port_base_t *port_obj = (mb_port_base_t *)*in_out_inst;
|
||||
ret = mb_port_ser_create(ser_opts, &port_obj);
|
||||
MB_GOTO_ON_FALSE((ret == MB_ENOERR), MB_EILLSTATE, error, TAG, "serial port creation, err: %d", ret);
|
||||
ret = mb_port_tmr_create(port_obj, MB_RTU_GET_T35_VAL(ser_opts->baudrate));
|
||||
ret = mb_port_timer_create(port_obj, MB_RTU_GET_T35_VAL(ser_opts->baudrate));
|
||||
MB_GOTO_ON_FALSE((ret == MB_ENOERR), MB_EILLSTATE, error, TAG, "timer port creation, err: %d", ret);
|
||||
// Override default response time if defined
|
||||
if (ser_opts->response_tout_ms) {
|
||||
mb_port_tmr_set_response_time(port_obj, ser_opts->response_tout_ms);
|
||||
mb_port_timer_set_response_time(port_obj, ser_opts->response_tout_ms);
|
||||
}
|
||||
ret = mb_port_evt_create(port_obj);
|
||||
ret = mb_port_event_create(port_obj);
|
||||
MB_GOTO_ON_FALSE((ret == MB_ENOERR), MB_EILLSTATE, error, TAG, "event port creation, err: %d", ret);
|
||||
transp->base.port_obj = port_obj;
|
||||
// Set callback function pointer for the timer
|
||||
port_obj->cb.tmr_expired = mbm_rtu_transp_tmr_35_expired;
|
||||
port_obj->cb.tmr_expired = mbm_rtu_transp_timer_expired;
|
||||
port_obj->cb.tx_empty = NULL;
|
||||
port_obj->cb.byte_rcvd = NULL;
|
||||
port_obj->arg = (void *)transp;
|
||||
@ -88,10 +88,10 @@ mb_err_enum_t mbm_rtu_transp_create(mb_serial_opts_t *ser_opts, void **in_out_in
|
||||
|
||||
error:
|
||||
if (port_obj->timer_obj) {
|
||||
mb_port_tmr_delete(port_obj);
|
||||
mb_port_timer_delete(port_obj);
|
||||
}
|
||||
if (port_obj->event_obj) {
|
||||
mb_port_evt_delete(port_obj);
|
||||
mb_port_event_delete(port_obj);
|
||||
}
|
||||
if (port_obj) {
|
||||
mb_port_ser_delete(port_obj);
|
||||
@ -105,8 +105,8 @@ bool mbm_rtu_transp_delete(mb_trans_base_t *inst)
|
||||
{
|
||||
mbm_rtu_transp_t *transp = __containerof(inst, mbm_rtu_transp_t, base);
|
||||
mb_port_ser_delete(transp->base.port_obj);
|
||||
mb_port_tmr_delete(transp->base.port_obj);
|
||||
mb_port_evt_delete(transp->base.port_obj);
|
||||
mb_port_timer_delete(transp->base.port_obj);
|
||||
mb_port_event_delete(transp->base.port_obj);
|
||||
CRITICAL_SECTION_CLOSE(inst->lock);
|
||||
free(transp);
|
||||
return true;
|
||||
@ -118,17 +118,17 @@ static void mbm_rtu_transp_start(mb_trans_base_t *inst)
|
||||
transp->state = MB_RTU_STATE_INIT;
|
||||
CRITICAL_SECTION(inst->lock) {
|
||||
mb_port_ser_enable(inst->port_obj);
|
||||
mb_port_tmr_enable(inst->port_obj);
|
||||
mb_port_timer_enable(inst->port_obj);
|
||||
};
|
||||
/* No special startup required for RTU. */
|
||||
(void)mb_port_evt_post(transp->base.port_obj, EVENT(EV_READY));
|
||||
(void)mb_port_event_post(transp->base.port_obj, EVENT(EV_READY));
|
||||
}
|
||||
|
||||
static void mbm_rtu_transp_stop(mb_trans_base_t *inst)
|
||||
{
|
||||
CRITICAL_SECTION(inst->lock) {
|
||||
mb_port_ser_disable(inst->port_obj);
|
||||
mb_port_tmr_disable(inst->port_obj);
|
||||
mb_port_timer_disable(inst->port_obj);
|
||||
};
|
||||
}
|
||||
|
||||
@ -206,9 +206,9 @@ static mb_err_enum_t mbm_rtu_transp_send(mb_trans_base_t *inst, uint8_t slv_addr
|
||||
// If the frame is broadcast, master will enable timer of convert delay,
|
||||
// else master will enable timer of respond timeout. */
|
||||
if (transp->frame_is_broadcast) {
|
||||
mb_port_tmr_convert_delay_enable(transp->base.port_obj);
|
||||
mb_port_timer_convert_delay_enable(transp->base.port_obj);
|
||||
} else {
|
||||
mb_port_tmr_respond_timeout_enable(transp->base.port_obj);
|
||||
mb_port_timer_respond_timeout_enable(transp->base.port_obj);
|
||||
}
|
||||
|
||||
} else {
|
||||
@ -230,35 +230,35 @@ static bool mbm_rtu_transp_snd_fsm(mb_trans_base_t *inst)
|
||||
}
|
||||
|
||||
|
||||
static bool mbm_rtu_transp_tmr_35_expired(void *inst)
|
||||
static bool mbm_rtu_transp_timer_expired(void *inst)
|
||||
{
|
||||
mbm_rtu_transp_t *transp = __containerof(inst, mbm_rtu_transp_t, base);
|
||||
|
||||
bool need_poll = false;
|
||||
mb_tmr_mode_enum_t timer_mode = mb_port_get_cur_tmr_mode(transp->base.port_obj);
|
||||
mb_timer_mode_enum_t timer_mode = mb_port_get_cur_timer_mode(transp->base.port_obj);
|
||||
|
||||
mb_port_tmr_disable(transp->base.port_obj);
|
||||
mb_port_timer_disable(transp->base.port_obj);
|
||||
|
||||
switch(timer_mode) {
|
||||
case MB_TMODE_T35:
|
||||
//need_poll = mb_port_evt_post(transp->base.port_obj, EVENT(EV_READY));
|
||||
//need_poll = mb_port_event_post(transp->base.port_obj, EVENT(EV_READY));
|
||||
//ESP_EARLY_LOGD(TAG, "%p:EV_READY", transp->base.descr.parent);
|
||||
break;
|
||||
|
||||
case MB_TMODE_RESPOND_TIMEOUT:
|
||||
mb_port_evt_set_err_type(transp->base.port_obj, EV_ERROR_RESPOND_TIMEOUT);
|
||||
need_poll = mb_port_evt_post(transp->base.port_obj, EVENT(EV_ERROR_PROCESS));
|
||||
mb_port_event_set_err_type(transp->base.port_obj, EV_ERROR_RESPOND_TIMEOUT);
|
||||
need_poll = mb_port_event_post(transp->base.port_obj, EVENT(EV_ERROR_PROCESS));
|
||||
ESP_EARLY_LOGW(TAG, "%p:EV_ERROR_RESPOND_TIMEOUT", transp->base.descr.parent);
|
||||
break;
|
||||
|
||||
case MB_TMODE_CONVERT_DELAY:
|
||||
/* If timer mode is convert delay, the master event then turns EV_MASTER_EXECUTE status. */
|
||||
need_poll = mb_port_evt_post(transp->base.port_obj, EVENT(EV_EXECUTE));
|
||||
need_poll = mb_port_event_post(transp->base.port_obj, EVENT(EV_EXECUTE));
|
||||
ESP_EARLY_LOGD(TAG, "%p:MB_TMODE_CONVERT_DELAY", transp->base.descr.parent);
|
||||
break;
|
||||
|
||||
default:
|
||||
need_poll = mb_port_evt_post(transp->base.port_obj, EVENT(EV_READY));
|
||||
need_poll = mb_port_event_post(transp->base.port_obj, EVENT(EV_READY));
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ typedef struct
|
||||
uint8_t *snd_buf_cur;
|
||||
uint16_t snd_buf_cnt;
|
||||
uint16_t rcv_buf_pos;
|
||||
volatile mb_tmr_mode_enum_t cur_tmr_mode;
|
||||
volatile mb_timer_mode_enum_t cur_timer_mode;
|
||||
mb_rtu_state_enum_t state;
|
||||
} mbs_rtu_transp_t;
|
||||
|
||||
@ -32,7 +32,7 @@ static mb_err_enum_t mbs_rtu_transp_receive(mb_trans_base_t *inst, uint8_t *rcv_
|
||||
static mb_err_enum_t mbs_rtu_transp_send(mb_trans_base_t *inst, uint8_t slv_addr, const uint8_t *frame_ptr, uint16_t len);
|
||||
static bool mbs_rtu_transp_rcv_fsm(mb_trans_base_t *inst);
|
||||
static bool mbs_rtu_transp_snd_fsm(mb_trans_base_t *inst);
|
||||
static bool mbs_rtu_transp_tmr_35_expired(void *inst);
|
||||
static bool mbs_rtu_transp_timer_expired(void *inst);
|
||||
static void mbs_rtu_transp_get_snd_buf(mb_trans_base_t *inst, uint8_t **frame_ptr_buf);
|
||||
void mbs_rtu_transp_get_rcv_buf(mb_trans_base_t *inst, uint8_t **frame_ptr_buf);
|
||||
static uint16_t mbs_rtu_transp_get_snd_len(mb_trans_base_t *inst);
|
||||
@ -61,13 +61,13 @@ mb_err_enum_t mbs_rtu_transp_create(mb_serial_opts_t *ser_opts, void **in_out_in
|
||||
mb_port_base_t *port_obj = (mb_port_base_t *)*in_out_inst;
|
||||
ret = mb_port_ser_create(ser_opts, &port_obj);
|
||||
MB_GOTO_ON_FALSE((ret == MB_ENOERR), MB_EPORTERR, error, TAG, "serial port creation, err: %d", ret);
|
||||
ret = mb_port_tmr_create(port_obj, MB_RTU_GET_T35_VAL(ser_opts->baudrate));
|
||||
ret = mb_port_timer_create(port_obj, MB_RTU_GET_T35_VAL(ser_opts->baudrate));
|
||||
MB_GOTO_ON_FALSE((ret == MB_ENOERR), MB_EPORTERR, error, TAG, "timer port creation, err: %d", ret);
|
||||
ret = mb_port_evt_create(port_obj);
|
||||
ret = mb_port_event_create(port_obj);
|
||||
MB_GOTO_ON_FALSE((ret == MB_ENOERR), MB_EPORTERR, error, TAG, "event port creation, err: %d", ret);
|
||||
transp->base.port_obj = port_obj;
|
||||
// Set callback function pointer for the timer
|
||||
port_obj->cb.tmr_expired = mbs_rtu_transp_tmr_35_expired;
|
||||
port_obj->cb.tmr_expired = mbs_rtu_transp_timer_expired;
|
||||
port_obj->cb.tx_empty = NULL;
|
||||
port_obj->cb.byte_rcvd = NULL;
|
||||
port_obj->arg = (void *)transp;
|
||||
@ -94,8 +94,8 @@ bool mbs_rtu_transp_delete(mb_trans_base_t *inst)
|
||||
mbs_rtu_transp_t *transp = __containerof(inst, mbs_rtu_transp_t, base);
|
||||
CRITICAL_SECTION(inst->lock) {
|
||||
mb_port_ser_delete(transp->base.port_obj);
|
||||
mb_port_tmr_delete(transp->base.port_obj);
|
||||
mb_port_evt_delete(transp->base.port_obj);
|
||||
mb_port_timer_delete(transp->base.port_obj);
|
||||
mb_port_event_delete(transp->base.port_obj);
|
||||
}
|
||||
CRITICAL_SECTION_CLOSE(inst->lock);
|
||||
free(transp);
|
||||
@ -108,16 +108,16 @@ static void mbs_rtu_transp_start(mb_trans_base_t *inst)
|
||||
transp->state = MB_RTU_STATE_INIT;
|
||||
CRITICAL_SECTION(inst->lock) {
|
||||
mb_port_ser_enable(inst->port_obj);
|
||||
//mb_port_tmr_enable(inst->port_obj);
|
||||
//mb_port_timer_enable(inst->port_obj);
|
||||
};
|
||||
(void)mb_port_evt_post(transp->base.port_obj, EVENT(EV_READY));
|
||||
(void)mb_port_event_post(transp->base.port_obj, EVENT(EV_READY));
|
||||
}
|
||||
|
||||
static void mbs_rtu_transp_stop(mb_trans_base_t *inst)
|
||||
{
|
||||
CRITICAL_SECTION(inst->lock) {
|
||||
mb_port_ser_disable(inst->port_obj);
|
||||
mb_port_tmr_disable(inst->port_obj);
|
||||
mb_port_timer_disable(inst->port_obj);
|
||||
};
|
||||
}
|
||||
|
||||
@ -208,14 +208,13 @@ static bool mbs_rtu_transp_snd_fsm(mb_trans_base_t *inst)
|
||||
return false;
|
||||
}
|
||||
|
||||
IRAM_ATTR
|
||||
static bool mbs_rtu_transp_tmr_35_expired(void *inst)
|
||||
static bool mbs_rtu_transp_timer_expired(void *inst)
|
||||
{
|
||||
mbs_rtu_transp_t *transp = __containerof(inst, mbs_rtu_transp_t, base);
|
||||
bool need_poll = false;
|
||||
//mb_tmr_mode_enum_t timer_mode = mb_port_get_cur_tmr_mode(transp->base.port_obj);
|
||||
//mb_timer_mode_enum_t timer_mode = mb_port_get_cur_timer_mode(transp->base.port_obj);
|
||||
|
||||
mb_port_tmr_disable(transp->base.port_obj);
|
||||
mb_port_timer_disable(transp->base.port_obj);
|
||||
|
||||
return need_poll;
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ extern "C" {
|
||||
|
||||
/* ----------------------- Defines ------------------------------------------*/
|
||||
#define MB_RTU_SER_PDU_SIZE_MIN 4 /*!< Minimum size of a Modbus RTU frame. */
|
||||
#define MB_RTU_SER_PDU_SIZE_MAX MB_BUFFER_SIZE /*!< Maximum size of a Modbus RTU frame. */
|
||||
#define MB_RTU_SER_PDU_SIZE_MAX MB_BUFFER_SIZE /*!< Maximum size of a Modbus RTU frame. */
|
||||
#define MB_RTU_SER_PDU_SIZE_CRC 2 /*!< Size of CRC field in PDU. */
|
||||
#define MB_RTU_SER_PDU_ADDR_OFF 0 /*!< Offset of slave address in Ser-PDU. */
|
||||
#define MB_RTU_SER_PDU_PDU_OFF 1 /*!< Offset of Modbus-PDU in Ser-PDU. */
|
||||
|
@ -34,7 +34,6 @@ static void mbm_tcp_transp_get_rcv_buf(mb_trans_base_t *inst, uint8_t **frame_pt
|
||||
static void mbm_tcp_transp_get_snd_buf(mb_trans_base_t *inst, uint8_t **frame_ptr_buf);
|
||||
bool mbm_tcp_transp_delete(mb_trans_base_t *inst);
|
||||
static bool mbm_tcp_transp_rq_is_bcast(mb_trans_base_t *inst);
|
||||
//static bool mbm_tcp_transp_tmr_expired(void *inst);
|
||||
|
||||
mb_err_enum_t mbm_tcp_transp_create(mb_tcp_opts_t *tcp_opts, void **in_out_inst)
|
||||
{
|
||||
@ -58,16 +57,16 @@ mb_err_enum_t mbm_tcp_transp_create(mb_tcp_opts_t *tcp_opts, void **in_out_inst)
|
||||
mb_port_base_t *port_obj = (mb_port_base_t *)*in_out_inst;
|
||||
ret = mbm_port_tcp_create(tcp_opts, &port_obj);
|
||||
MB_GOTO_ON_FALSE((ret == MB_ENOERR), MB_EPORTERR, error, TAG, "port creation, err: %d", ret);
|
||||
ret = mb_port_tmr_create(port_obj, MB_TCP_TIMEOUT_MS * MB_TIMER_TICS_PER_MS);
|
||||
ret = mb_port_timer_create(port_obj, MB_TCP_TIMEOUT_MS * MB_TIMER_TICS_PER_MS);
|
||||
MB_GOTO_ON_FALSE((ret == MB_ENOERR), MB_EPORTERR, error, TAG, "timer port creation, err: %d", ret);
|
||||
// Override default response time if defined
|
||||
if (tcp_opts->response_tout_ms) {
|
||||
mb_port_tmr_set_response_time(port_obj, tcp_opts->response_tout_ms);
|
||||
mb_port_timer_set_response_time(port_obj, tcp_opts->response_tout_ms);
|
||||
}
|
||||
ret = mb_port_evt_create(port_obj);
|
||||
ret = mb_port_event_create(port_obj);
|
||||
MB_GOTO_ON_FALSE((ret == MB_ENOERR), MB_EPORTERR, error, TAG, "event port creation, err: %d", ret);
|
||||
// Set callback function pointer for the timer
|
||||
// port_obj->cb.tmr_expired = mbm_tcp_transp_tmr_expired;
|
||||
// port_obj->cb.tmr_expired = mbm_tcp_transp_timer_expired;
|
||||
// port_obj->cb.tx_empty = NULL;
|
||||
// port_obj->cb.byte_rcvd = NULL;
|
||||
// port_obj->arg = (void *)transp;
|
||||
@ -83,6 +82,7 @@ error:
|
||||
free(port_obj->timer_obj);
|
||||
}
|
||||
free(port_obj);
|
||||
CRITICAL_SECTION_UNLOCK(transp->base.lock);
|
||||
CRITICAL_SECTION_CLOSE(transp->base.lock);
|
||||
free(transp);
|
||||
return ret;
|
||||
@ -94,8 +94,8 @@ bool mbm_tcp_transp_delete(mb_trans_base_t *inst)
|
||||
// destroy method of port tcp master is here
|
||||
CRITICAL_SECTION(inst->lock) {
|
||||
mbm_port_tcp_delete(inst->port_obj);
|
||||
mb_port_tmr_delete(inst->port_obj);
|
||||
mb_port_evt_delete(inst->port_obj);
|
||||
mb_port_timer_delete(inst->port_obj);
|
||||
mb_port_event_delete(inst->port_obj);
|
||||
}
|
||||
CRITICAL_SECTION_CLOSE(inst->lock);
|
||||
free(transp);
|
||||
@ -108,10 +108,10 @@ static void mbm_tcp_transp_start(mb_trans_base_t *inst)
|
||||
transp->state = MB_TCP_STATE_INIT;
|
||||
CRITICAL_SECTION(inst->lock) {
|
||||
mbm_port_tcp_enable(inst->port_obj);
|
||||
mb_port_tmr_enable(inst->port_obj);
|
||||
mb_port_timer_enable(inst->port_obj);
|
||||
};
|
||||
/* No special startup required for TCP. */
|
||||
(void)mb_port_evt_post(inst->port_obj, EVENT(EV_READY));
|
||||
(void)mb_port_event_post(inst->port_obj, EVENT(EV_READY));
|
||||
}
|
||||
|
||||
static void mbm_tcp_transp_stop(mb_trans_base_t *inst)
|
||||
@ -119,7 +119,7 @@ static void mbm_tcp_transp_stop(mb_trans_base_t *inst)
|
||||
/* Make sure that no more clients are connected. */
|
||||
CRITICAL_SECTION(inst->lock) {
|
||||
mbm_port_tcp_disable(inst->port_obj);
|
||||
mb_port_tmr_disable(inst->port_obj);
|
||||
mb_port_timer_disable(inst->port_obj);
|
||||
};
|
||||
}
|
||||
|
||||
@ -181,18 +181,10 @@ static mb_err_enum_t mbm_tcp_transp_send(mb_trans_base_t *inst, uint8_t address,
|
||||
if (mbm_port_tcp_send_data(inst->port_obj, address, frame_ptr, tcp_len) == false) {
|
||||
status = MB_EIO;
|
||||
}
|
||||
mb_port_tmr_respond_timeout_enable(inst->port_obj);
|
||||
mb_port_timer_respond_timeout_enable(inst->port_obj);
|
||||
return status;
|
||||
}
|
||||
|
||||
// IRAM_ATTR
|
||||
// static bool mbm_tcp_transp_tmr_expired(void *inst)
|
||||
// {
|
||||
// mbm_tcp_transp_t *transp = __containerof(inst, mbm_tcp_transp_t, base);
|
||||
|
||||
// return mbm_port_timer_expired(transp->base.port_obj);
|
||||
// }
|
||||
|
||||
static void mbm_tcp_transp_get_rcv_buf(mb_trans_base_t *inst, uint8_t **frame_ptr_buf)
|
||||
{
|
||||
mbm_tcp_transp_t *transp = __containerof(inst, mbm_tcp_transp_t, base);
|
||||
|
@ -31,7 +31,7 @@ static mb_err_enum_t mbs_tcp_transp_send(mb_trans_base_t *inst, uint8_t _unused,
|
||||
static void mbs_tcp_transp_get_rcv_buf(mb_trans_base_t *inst, uint8_t **frame_ptr_buf);
|
||||
static void mbs_tcp_transp_get_snd_buf(mb_trans_base_t *inst, uint8_t **frame_ptr_buf);
|
||||
bool mbs_tcp_transp_delete(mb_trans_base_t *inst);
|
||||
static bool mbs_tcp_transp_tmr_expired(void *inst);
|
||||
static bool mbs_tcp_transp_timer_expired(void *inst);
|
||||
|
||||
mb_err_enum_t mbs_tcp_transp_create(mb_tcp_opts_t *tcp_opts, void **in_out_inst)
|
||||
{
|
||||
@ -55,17 +55,17 @@ mb_err_enum_t mbs_tcp_transp_create(mb_tcp_opts_t *tcp_opts, void **in_out_inst)
|
||||
mb_port_base_t *port_obj = (mb_port_base_t *)*in_out_inst;
|
||||
ret = mbs_port_tcp_create(tcp_opts, &port_obj);
|
||||
MB_GOTO_ON_FALSE((ret == MB_ENOERR), MB_EPORTERR, error, TAG, "tcp port creation, err: %d", ret);
|
||||
ret = mb_port_tmr_create(port_obj, MB_TCP_TIMEOUT_MS * MB_TIMER_TICS_PER_MS);
|
||||
ret = mb_port_timer_create(port_obj, MB_TCP_TIMEOUT_MS * MB_TIMER_TICS_PER_MS);
|
||||
MB_GOTO_ON_FALSE((ret == MB_ENOERR), MB_EPORTERR, error, TAG, "timer port creation, err: %d", ret);
|
||||
// Override default response time if defined
|
||||
if (tcp_opts->response_tout_ms) {
|
||||
mb_port_tmr_set_response_time(port_obj, tcp_opts->response_tout_ms);
|
||||
mb_port_timer_set_response_time(port_obj, tcp_opts->response_tout_ms);
|
||||
}
|
||||
ret = mb_port_evt_create(port_obj);
|
||||
ret = mb_port_event_create(port_obj);
|
||||
MB_GOTO_ON_FALSE((ret == MB_ENOERR), MB_EPORTERR, error, TAG, "event port creation, err: %d", ret);
|
||||
transp->base.port_obj = port_obj;
|
||||
// Set callback function pointer for the timer
|
||||
port_obj->cb.tmr_expired = mbs_tcp_transp_tmr_expired;
|
||||
port_obj->cb.tmr_expired = mbs_tcp_transp_timer_expired;
|
||||
port_obj->cb.tx_empty = NULL;
|
||||
port_obj->cb.byte_rcvd = NULL;
|
||||
port_obj->arg = (void *)transp;
|
||||
@ -80,6 +80,7 @@ error:
|
||||
free(port_obj->timer_obj);
|
||||
}
|
||||
free(port_obj);
|
||||
CRITICAL_SECTION_UNLOCK(transp->base.lock);
|
||||
CRITICAL_SECTION_CLOSE(transp->base.lock);
|
||||
free(transp);
|
||||
return ret;
|
||||
@ -91,8 +92,8 @@ bool mbs_tcp_transp_delete(mb_trans_base_t *inst)
|
||||
// destroy method of port tcp slave is here
|
||||
CRITICAL_SECTION(inst->lock) {
|
||||
mbs_port_tcp_delete(inst->port_obj);
|
||||
mb_port_tmr_delete(inst->port_obj);
|
||||
mb_port_evt_delete(inst->port_obj);
|
||||
mb_port_timer_delete(inst->port_obj);
|
||||
mb_port_event_delete(inst->port_obj);
|
||||
}
|
||||
CRITICAL_SECTION_CLOSE(inst->lock);
|
||||
free(transp);
|
||||
@ -103,10 +104,10 @@ static void mbs_tcp_transp_start(mb_trans_base_t *inst)
|
||||
{
|
||||
CRITICAL_SECTION(inst->lock) {
|
||||
mbs_port_tcp_enable(inst->port_obj);
|
||||
mb_port_tmr_enable(inst->port_obj);
|
||||
mb_port_timer_enable(inst->port_obj);
|
||||
};
|
||||
/* No special startup required for TCP. */
|
||||
(void)mb_port_evt_post(inst->port_obj, EVENT(EV_READY));
|
||||
(void)mb_port_event_post(inst->port_obj, EVENT(EV_READY));
|
||||
}
|
||||
|
||||
static void mbs_tcp_transp_stop(mb_trans_base_t *inst)
|
||||
@ -114,24 +115,30 @@ static void mbs_tcp_transp_stop(mb_trans_base_t *inst)
|
||||
/* Make sure that no more clients are connected. */
|
||||
CRITICAL_SECTION(inst->lock) {
|
||||
mbs_port_tcp_disable(inst->port_obj);
|
||||
mb_port_tmr_disable(inst->port_obj);
|
||||
mb_port_timer_disable(inst->port_obj);
|
||||
};
|
||||
}
|
||||
|
||||
static mb_err_enum_t mbs_tcp_transp_receive(mb_trans_base_t *inst, uint8_t *rcv_addr, uint8_t **frame_ptr_buf, uint16_t *pbuf_len)
|
||||
{
|
||||
if (!pbuf_len || !frame_ptr_buf || !pbuf_len) {
|
||||
return MB_EIO;
|
||||
}
|
||||
|
||||
mbs_tcp_transp_t *transp = __containerof(inst, mbs_tcp_transp_t, base);
|
||||
|
||||
uint8_t *frame_ptr = (uint8_t *)transp->recv_buf;
|
||||
uint16_t length = *pbuf_len;
|
||||
mb_err_enum_t status = MB_EIO;
|
||||
uint8_t *frame_ptr;
|
||||
uint16_t len = *pbuf_len;
|
||||
uint16_t pid;
|
||||
|
||||
if (mbs_port_tcp_recv_data(inst->port_obj, &frame_ptr, &len) != false) {
|
||||
if (mbs_port_tcp_recv_data(inst->port_obj, &frame_ptr, &length) != false) {
|
||||
pid = frame_ptr[MB_TCP_PID] << 8U;
|
||||
pid |= frame_ptr[MB_TCP_PID + 1];
|
||||
|
||||
if (pid == MB_TCP_PROTOCOL_ID) {
|
||||
*frame_ptr_buf = &frame_ptr[MB_TCP_FUNC];
|
||||
*pbuf_len = len - MB_TCP_FUNC;
|
||||
*pbuf_len = length - MB_TCP_FUNC;
|
||||
status = MB_ENOERR;
|
||||
|
||||
/* Get MBAP UID field if its support is enabled.
|
||||
@ -170,36 +177,35 @@ static mb_err_enum_t mbs_tcp_transp_send(mb_trans_base_t *inst, uint8_t _unused,
|
||||
return status;
|
||||
}
|
||||
|
||||
__attribute__((unused))
|
||||
static bool mbs_tcp_transp_tmr_expired(void *inst)
|
||||
static bool mbs_tcp_transp_timer_expired(void *inst)
|
||||
{
|
||||
mbs_tcp_transp_t *transp = __containerof(inst, mbs_tcp_transp_t, base);
|
||||
|
||||
bool need_poll = false;
|
||||
mb_tmr_mode_enum_t timer_mode = mb_port_get_cur_tmr_mode(transp->base.port_obj);
|
||||
mb_timer_mode_enum_t timer_mode = mb_port_get_cur_timer_mode(transp->base.port_obj);
|
||||
|
||||
mb_port_tmr_disable(transp->base.port_obj);
|
||||
mb_port_timer_disable(transp->base.port_obj);
|
||||
|
||||
switch(timer_mode) {
|
||||
case MB_TMODE_T35:
|
||||
need_poll = mb_port_evt_post(transp->base.port_obj, EVENT(EV_READY));
|
||||
need_poll = mb_port_event_post(transp->base.port_obj, EVENT(EV_READY));
|
||||
ESP_EARLY_LOGD(TAG, "EV_READY");
|
||||
break;
|
||||
|
||||
case MB_TMODE_RESPOND_TIMEOUT:
|
||||
mb_port_evt_set_err_type(transp->base.port_obj, EV_ERROR_RESPOND_TIMEOUT);
|
||||
need_poll = mb_port_evt_post(transp->base.port_obj, EVENT(EV_ERROR_PROCESS));
|
||||
mb_port_event_set_err_type(transp->base.port_obj, EV_ERROR_RESPOND_TIMEOUT);
|
||||
need_poll = mb_port_event_post(transp->base.port_obj, EVENT(EV_ERROR_PROCESS));
|
||||
ESP_EARLY_LOGD(TAG, "EV_ERROR_RESPOND_TIMEOUT");
|
||||
break;
|
||||
|
||||
case MB_TMODE_CONVERT_DELAY:
|
||||
/* If timer mode is convert delay, the master event then turns EV_MASTER_EXECUTE status. */
|
||||
need_poll = mb_port_evt_post(transp->base.port_obj, EVENT(EV_EXECUTE));
|
||||
need_poll = mb_port_event_post(transp->base.port_obj, EVENT(EV_EXECUTE));
|
||||
ESP_EARLY_LOGD(TAG, "MB_TMODE_CONVERT_DELAY");
|
||||
break;
|
||||
|
||||
default:
|
||||
need_poll = mb_port_evt_post(transp->base.port_obj, EVENT(EV_READY));
|
||||
need_poll = mb_port_event_post(transp->base.port_obj, EVENT(EV_READY));
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -25,12 +25,18 @@ markers =
|
||||
esp32s3: support esp32s3 target
|
||||
esp32c3: support esp32c3 target
|
||||
esp32c2: support esp32c2 target
|
||||
esp32p4: support esp32p4 target
|
||||
esp32c5: support esp32c5 target
|
||||
|
||||
# special markers
|
||||
temp_skip_ci: temp skip tests for specified targets only in ci
|
||||
|
||||
# env markers
|
||||
generic: tests should be run on generic runners
|
||||
|
||||
# multi-dut markers
|
||||
multi_dut_generic: tests should be run on generic runners, at least have two duts connected.
|
||||
multi_dut_modbus_generic: generic Modbus dut
|
||||
multi_dut_modbus_tcp: Modbus TCP runners with two duts connected
|
||||
multi_dut_modbus_rs485: Modbus RTU/ASCII runners with two duts connected
|
||||
multi_dut_modbus_serial: Alias for Modbus RTU/ASCII runners with two duts connected
|
||||
|
@ -1,14 +1,16 @@
|
||||
physical_tests:
|
||||
disable_test:
|
||||
- if: IDF_TARGET != "esp32"
|
||||
reason: only manual test is performed for other targets
|
||||
disable:
|
||||
- if: CONFIG_NAME != "serial" or IDF_TARGET != "esp32"
|
||||
# disable_test:
|
||||
# - if: IDF_TARGET != "esp32"
|
||||
# reason: only manual test is performed for other targets
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
- if: CONFIG_NAME == "wifi" and SOC_WIFI_SUPPORTED != 1 and IDF_TARGET != "esp32"
|
||||
|
||||
unit_tests:
|
||||
disable_test:
|
||||
- if: IDF_TARGET != "esp32"
|
||||
reason: only manual test is performed for other targets
|
||||
|
||||
adapter_tests:
|
||||
disable_test:
|
||||
- if: IDF_TARGET != "esp32"
|
||||
reason: only manual test is performed for other targets
|
@ -1,5 +1,6 @@
|
||||
set(srcs "test_app_main.c"
|
||||
"test_modbus_adapter_comm.c"
|
||||
"test_modbus_adapter_serial.c"
|
||||
"test_modbus_adapter_tcp.c"
|
||||
)
|
||||
|
||||
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
|
||||
@ -7,4 +8,5 @@ idf_component_register(SRCS ${srcs}
|
||||
PRIV_REQUIRES cmock test_common unity test_utils
|
||||
) #
|
||||
|
||||
set_property(TARGET ${COMPONENT_LIB} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-u mb_test_include_impl")
|
||||
set_property(TARGET ${COMPONENT_LIB} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-u mb_test_include_adapter_impl_serial")
|
||||
set_property(TARGET ${COMPONENT_LIB} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-u mb_test_include_adapter_impl_tcp")
|
||||
|
@ -1,6 +1,6 @@
|
||||
dependencies:
|
||||
idf: ">=4.3"
|
||||
espressif/esp-modbus:
|
||||
version: "^2.0"
|
||||
version: "^2.0.0-beta"
|
||||
override_path: "../../../"
|
||||
|
||||
|
@ -8,9 +8,16 @@
|
||||
#include "unity_test_runner.h"
|
||||
#include "unity_fixture.h"
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
static void run_all_tests(void)
|
||||
{
|
||||
RUN_TEST_GROUP(modbus_adapter_comm_basic);
|
||||
#if (CONFIG_FMB_COMM_MODE_RTU_EN || CONFIG_FMB_COMM_MODE_ASCII_EN)
|
||||
RUN_TEST_GROUP(modbus_adapter_serial);
|
||||
#endif
|
||||
#if (CONFIG_FMB_COMM_MODE_TCP_EN)
|
||||
RUN_TEST_GROUP(modbus_adapter_tcp);
|
||||
#endif
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
|
@ -1,249 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include "unity_fixture.h"
|
||||
|
||||
#include "test_utils.h"
|
||||
|
||||
#if __has_include("unity_test_utils.h")
|
||||
#define UNITY_TEST_UTILS_INCLUDED
|
||||
// unity test utils are used
|
||||
#include "unity_test_utils.h"
|
||||
#include "unity_test_utils_memory.h"
|
||||
#else
|
||||
// Unit_test_app utils from test_utils ("test_utils.h"), v4.4
|
||||
#define unity_utils_task_delete test_utils_task_delete
|
||||
#endif
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "test_common.h"
|
||||
|
||||
#define TEST_SER_PORT_NUM 1
|
||||
#define TEST_TCP_PORT_NUM 1502
|
||||
#define TEST_TASKS_NUM 3
|
||||
#define TEST_TASK_TIMEOUT_MS 30000
|
||||
#define TEST_LEAK_WARN 32
|
||||
#define TEST_LEAK_CRITICAL 64
|
||||
#define TEST_SLAVE_SEND_TOUT_US 30000
|
||||
#define TEST_MASTER_SEND_TOUT_US 30000
|
||||
|
||||
#define TEST_MASTER_RESPOND_TOUT_MS CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND
|
||||
|
||||
#define TAG "MODBUS_SERIAL_TEST"
|
||||
|
||||
// The workaround to statically link whole test library
|
||||
__attribute__((unused)) bool mb_test_include_impl = 1;
|
||||
|
||||
// Example Data (Object) Dictionary for Modbus parameters
|
||||
static const mb_parameter_descriptor_t descriptors[] = {
|
||||
{CID_DEV_REG0, STR("MB_hold_reg-0"), STR("Data"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0, 1,
|
||||
0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE_TRIGGER},
|
||||
{CID_DEV_REG1, STR("MB_hold_reg-1"), STR("Data"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 1, 1,
|
||||
0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE_TRIGGER},
|
||||
{CID_DEV_REG2, STR("MB_hold_reg-2"), STR("Data"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 2, 1,
|
||||
0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE_TRIGGER},
|
||||
{CID_DEV_REG3, STR("MB_hold_reg-3"), STR("Data"), MB_DEVICE_ADDR2, MB_PARAM_HOLDING, 3, 1,
|
||||
0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE_TRIGGER},
|
||||
{CID_DEV_REG_COUNT, STR("CYCLE_COUNTER"), STR("Data"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 4, 1,
|
||||
0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE_TRIGGER}
|
||||
};
|
||||
|
||||
// Calculate number of parameters in the table
|
||||
const uint16_t num_descriptors = (sizeof(descriptors) / sizeof(descriptors[0]));
|
||||
|
||||
TaskHandle_t task_handles[TEST_TASKS_NUM] = {};
|
||||
|
||||
TEST_GROUP(modbus_adapter_comm_basic);
|
||||
|
||||
TEST_SETUP(modbus_adapter_comm_basic)
|
||||
{
|
||||
test_common_start();
|
||||
}
|
||||
|
||||
TEST_TEAR_DOWN(modbus_adapter_comm_basic)
|
||||
{
|
||||
uint32_t test_task = 0;
|
||||
uint32_t test_task_count = 0;
|
||||
int i = 0;
|
||||
|
||||
// Trigger start of test task intentionally
|
||||
for (i = 0; i < TEST_TASKS_NUM; i++) {
|
||||
test_common_task_notify_start(task_handles[i], 1);
|
||||
}
|
||||
|
||||
for (i = 0; (i < TEST_TASKS_NUM); i++) {
|
||||
test_task = test_common_wait_done(pdMS_TO_TICKS(TEST_TASK_TIMEOUT_MS));
|
||||
if (test_task) {
|
||||
unity_utils_task_delete((TaskHandle_t)test_task);
|
||||
ESP_LOGI(TAG, "Task %" PRIx32 " is complited.", test_task);
|
||||
test_task_count++;
|
||||
}
|
||||
}
|
||||
|
||||
vTaskDelay(5); // Let the test tasks with lower priority to suspend or delete itself from test_common
|
||||
|
||||
TEST_ASSERT_EQUAL(TEST_TASKS_NUM, test_task_count);
|
||||
ESP_LOGI(TAG, "Test done successfully.");
|
||||
|
||||
test_common_stop();
|
||||
}
|
||||
|
||||
#if (CONFIG_FMB_COMM_MODE_RTU_EN || CONFIG_FMB_COMM_MODE_ASCII_EN)
|
||||
|
||||
TEST(modbus_adapter_comm_basic, test_modbus_adapter_rtu)
|
||||
{
|
||||
mb_communication_info_t slave_config1 = {
|
||||
.ser_opts.port = TEST_SER_PORT_NUM,
|
||||
.ser_opts.mode = MB_RTU,
|
||||
.ser_opts.uid = MB_DEVICE_ADDR1,
|
||||
.ser_opts.data_bits = UART_DATA_8_BITS,
|
||||
.ser_opts.stop_bits = UART_STOP_BITS_2,
|
||||
.ser_opts.baudrate = 115200,
|
||||
.ser_opts.parity = UART_PARITY_DISABLE,
|
||||
.ser_opts.response_tout_ms = 1,
|
||||
.ser_opts.test_tout_us = TEST_SLAVE_SEND_TOUT_US
|
||||
};
|
||||
|
||||
task_handles[0] = test_slave_serial_create(&slave_config1);
|
||||
|
||||
mb_communication_info_t slave_config2 = {
|
||||
.ser_opts.port = TEST_SER_PORT_NUM,
|
||||
.ser_opts.mode = MB_RTU,
|
||||
.ser_opts.uid = MB_DEVICE_ADDR2,
|
||||
.ser_opts.data_bits = UART_DATA_8_BITS,
|
||||
.ser_opts.stop_bits = UART_STOP_BITS_2,
|
||||
.ser_opts.baudrate = 115200,
|
||||
.ser_opts.parity = UART_PARITY_DISABLE,
|
||||
.ser_opts.response_tout_ms = 1,
|
||||
.ser_opts.test_tout_us = TEST_SLAVE_SEND_TOUT_US
|
||||
};
|
||||
|
||||
task_handles[1] = test_slave_serial_create(&slave_config2);
|
||||
|
||||
// Initialize and start Modbus controller
|
||||
mb_communication_info_t master_config = {
|
||||
.ser_opts.port = TEST_SER_PORT_NUM,
|
||||
.ser_opts.mode = MB_RTU,
|
||||
.ser_opts.data_bits = UART_DATA_8_BITS,
|
||||
.ser_opts.stop_bits = UART_STOP_BITS_2,
|
||||
.ser_opts.baudrate = 115200,
|
||||
.ser_opts.parity = UART_PARITY_DISABLE,
|
||||
.ser_opts.response_tout_ms = TEST_MASTER_RESPOND_TOUT_MS,
|
||||
.ser_opts.test_tout_us = TEST_MASTER_SEND_TOUT_US
|
||||
};
|
||||
|
||||
task_handles[2] = test_master_serial_create(&master_config, &descriptors[0], num_descriptors);
|
||||
}
|
||||
|
||||
TEST(modbus_adapter_comm_basic, test_modbus_adapter_ascii)
|
||||
{
|
||||
mb_communication_info_t slave_config1 = {
|
||||
.ser_opts.port = TEST_SER_PORT_NUM,
|
||||
.ser_opts.mode = MB_ASCII,
|
||||
.ser_opts.uid = MB_DEVICE_ADDR1,
|
||||
.ser_opts.data_bits = UART_DATA_8_BITS,
|
||||
.ser_opts.stop_bits = UART_STOP_BITS_2,
|
||||
.ser_opts.baudrate = 115200,
|
||||
.ser_opts.parity = UART_PARITY_DISABLE,
|
||||
.ser_opts.response_tout_ms = 1,
|
||||
.ser_opts.test_tout_us = TEST_SLAVE_SEND_TOUT_US
|
||||
};
|
||||
|
||||
task_handles[0] = test_slave_serial_create(&slave_config1);
|
||||
|
||||
mb_communication_info_t slave_config2 = {
|
||||
.ser_opts.port = TEST_SER_PORT_NUM,
|
||||
.ser_opts.mode = MB_ASCII,
|
||||
.ser_opts.uid = MB_DEVICE_ADDR2,
|
||||
.ser_opts.data_bits = UART_DATA_8_BITS,
|
||||
.ser_opts.stop_bits = UART_STOP_BITS_2,
|
||||
.ser_opts.baudrate = 115200,
|
||||
.ser_opts.parity = UART_PARITY_DISABLE,
|
||||
.ser_opts.response_tout_ms = 1,
|
||||
.ser_opts.test_tout_us = TEST_SLAVE_SEND_TOUT_US
|
||||
};
|
||||
|
||||
task_handles[1] = test_slave_serial_create(&slave_config2);
|
||||
|
||||
mb_communication_info_t master_config = {
|
||||
.ser_opts.port = TEST_SER_PORT_NUM,
|
||||
.ser_opts.mode = MB_ASCII,
|
||||
.ser_opts.data_bits = UART_DATA_8_BITS,
|
||||
.ser_opts.stop_bits = UART_STOP_BITS_2,
|
||||
.ser_opts.baudrate = 115200,
|
||||
.ser_opts.parity = UART_PARITY_DISABLE,
|
||||
.ser_opts.response_tout_ms = TEST_MASTER_RESPOND_TOUT_MS,
|
||||
.ser_opts.test_tout_us = TEST_MASTER_SEND_TOUT_US
|
||||
};
|
||||
|
||||
task_handles[2] = test_master_serial_create(&master_config, &descriptors[0], num_descriptors);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if (CONFIG_FMB_COMM_MODE_TCP_EN)
|
||||
|
||||
const char *slave_tcp_addr_table[] = {
|
||||
"01;mb_slave_tcp_01;1502", // Corresponds to characteristic MB_DEVICE_ADDR1 "mb_slave_tcp_01"
|
||||
"200;mb_slave_tcp_c8;1502", // Corresponds to characteristic MB_DEVICE_ADDR2 "mb_slave_tcp_C8"
|
||||
NULL // End of table condition (must be included)
|
||||
};
|
||||
|
||||
TEST(modbus_adapter_comm_basic, test_modbus_adapter_tcp)
|
||||
{
|
||||
mb_communication_info_t tcp_slave_cfg_1 = {
|
||||
.tcp_opts.port = TEST_TCP_PORT_NUM,
|
||||
.tcp_opts.mode = MB_TCP,
|
||||
.tcp_opts.addr_type = MB_IPV4,
|
||||
.tcp_opts.ip_addr_table = NULL,
|
||||
.tcp_opts.uid = MB_DEVICE_ADDR1,
|
||||
.tcp_opts.start_disconnected = true,
|
||||
.tcp_opts.response_tout_ms = 1,
|
||||
.tcp_opts.test_tout_us = TEST_SLAVE_SEND_TOUT_US
|
||||
};
|
||||
|
||||
task_handles[0] = test_slave_tcp_create(&tcp_slave_cfg_1);
|
||||
|
||||
mb_communication_info_t tcp_slave_cfg_2 = {
|
||||
.tcp_opts.port = TEST_TCP_PORT_NUM,
|
||||
.tcp_opts.mode = MB_TCP,
|
||||
.tcp_opts.addr_type = MB_IPV4,
|
||||
.tcp_opts.ip_addr_table = NULL,
|
||||
.tcp_opts.uid = MB_DEVICE_ADDR2,
|
||||
.tcp_opts.start_disconnected = true,
|
||||
.tcp_opts.response_tout_ms = TEST_MASTER_RESPOND_TOUT_MS,
|
||||
.tcp_opts.test_tout_us = TEST_SLAVE_SEND_TOUT_US
|
||||
};
|
||||
|
||||
task_handles[1] = test_slave_tcp_create(&tcp_slave_cfg_2);
|
||||
|
||||
// Initialize and start Modbus controller
|
||||
mb_communication_info_t tcp_master_cfg_1 = {
|
||||
.tcp_opts.port = TEST_TCP_PORT_NUM,
|
||||
.tcp_opts.mode = MB_TCP,
|
||||
.tcp_opts.addr_type = MB_IPV4,
|
||||
.tcp_opts.ip_addr_table = (void *)slave_tcp_addr_table,
|
||||
.tcp_opts.uid = 0,
|
||||
.tcp_opts.start_disconnected = false,
|
||||
.tcp_opts.response_tout_ms = TEST_MASTER_RESPOND_TOUT_MS,
|
||||
.tcp_opts.test_tout_us = TEST_MASTER_SEND_TOUT_US
|
||||
};
|
||||
|
||||
task_handles[2] = test_master_tcp_create(&tcp_master_cfg_1, &descriptors[0], num_descriptors);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
TEST_GROUP_RUNNER(modbus_adapter_comm_basic)
|
||||
{
|
||||
#if (CONFIG_FMB_COMM_MODE_RTU_EN && CONFIG_FMB_COMM_MODE_ASCII_EN)
|
||||
RUN_TEST_CASE(modbus_adapter_comm_basic, test_modbus_adapter_rtu);
|
||||
RUN_TEST_CASE(modbus_adapter_comm_basic, test_modbus_adapter_ascii);
|
||||
#endif
|
||||
|
||||
#if (CONFIG_FMB_COMM_MODE_TCP_EN)
|
||||
RUN_TEST_CASE(modbus_adapter_comm_basic, test_modbus_adapter_tcp);
|
||||
#endif
|
||||
}
|
256
test_apps/adapter_tests/main/test_modbus_adapter_serial.c
Normal file
256
test_apps/adapter_tests/main/test_modbus_adapter_serial.c
Normal file
@ -0,0 +1,256 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include "unity_fixture.h"
|
||||
|
||||
#include "test_utils.h"
|
||||
|
||||
#if __has_include("unity_test_utils.h")
|
||||
#define UNITY_TEST_UTILS_INCLUDED
|
||||
// unity test utils are used
|
||||
#include "unity_test_utils.h"
|
||||
#include "unity_test_utils_memory.h"
|
||||
#else
|
||||
// Unit_test_app utils from test_utils ("test_utils.h"), v4.4
|
||||
#define unity_utils_task_delete test_utils_task_delete
|
||||
#endif
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "test_common.h"
|
||||
|
||||
#define TEST_SER_PORT_NUM1 (1)
|
||||
#define TEST_SER_PORT_NUM2 (2)
|
||||
#define TEST_TASK_TIMEOUT_MS (120000)
|
||||
#define TEST_LEAK_WARN (32)
|
||||
#define TEST_LEAK_CRITICAL (64)
|
||||
#define TEST_SLAVE_SEND_TOUT_US (5000)
|
||||
#define TEST_MASTER_SEND_TOUT_US (5000)
|
||||
|
||||
#define TEST_MASTER_RESPOND_TOUT_MS (CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND)
|
||||
|
||||
#define TAG "MODBUS_SERIAL_TEST"
|
||||
|
||||
// The workaround to statically link whole test library
|
||||
__attribute__((unused)) bool mb_test_include_adapter_impl_serial = true;
|
||||
|
||||
#if (CONFIG_FMB_COMM_MODE_RTU_EN || CONFIG_FMB_COMM_MODE_ASCII_EN)
|
||||
|
||||
// Example Data (Object) Dictionary for Modbus parameters
|
||||
static const mb_parameter_descriptor_t descriptors[] = {
|
||||
{CID_DEV_REG0, STR("MB_hold_reg-0"), STR("Data"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0, 1,
|
||||
0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE_TRIGGER},
|
||||
{CID_DEV_REG1, STR("MB_hold_reg-1"), STR("Data"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 1, 1,
|
||||
0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE_TRIGGER},
|
||||
{CID_DEV_REG2, STR("MB_hold_reg-2"), STR("Data"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 2, 1,
|
||||
0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE_TRIGGER},
|
||||
{CID_DEV_REG3, STR("MB_hold_reg-3"), STR("Data"), MB_DEVICE_ADDR2, MB_PARAM_HOLDING, 3, 1,
|
||||
0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE_TRIGGER},
|
||||
{CID_DEV_REG_COUNT, STR("CYCLE_COUNTER"), STR("Data"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 4, 1,
|
||||
0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE_TRIGGER}
|
||||
};
|
||||
|
||||
// Calculate number of parameters in the table
|
||||
const uint16_t num_descriptors = (sizeof(descriptors) / sizeof(descriptors[0]));
|
||||
|
||||
TEST_GROUP(modbus_adapter_serial);
|
||||
|
||||
TEST_SETUP(modbus_adapter_serial)
|
||||
{
|
||||
test_common_start();
|
||||
}
|
||||
|
||||
TEST_TEAR_DOWN(modbus_adapter_serial)
|
||||
{
|
||||
int task_count = test_common_task_start_all(1);
|
||||
TEST_ASSERT_TRUE(task_count > 0);
|
||||
TEST_ASSERT_EQUAL(task_count, test_common_task_wait_done_delete_all(TEST_TASK_TIMEOUT_MS));
|
||||
test_common_stop();
|
||||
ESP_LOGI(TAG, "%s, done successfully.", __func__);
|
||||
}
|
||||
|
||||
TEST(modbus_adapter_serial, test_modbus_adapter_rtu)
|
||||
{
|
||||
mb_communication_info_t slave_config1 = {
|
||||
.ser_opts.port = TEST_SER_PORT_NUM1,
|
||||
.ser_opts.mode = MB_RTU,
|
||||
.ser_opts.uid = MB_DEVICE_ADDR1,
|
||||
.ser_opts.data_bits = UART_DATA_8_BITS,
|
||||
.ser_opts.stop_bits = UART_STOP_BITS_2,
|
||||
.ser_opts.baudrate = 115200,
|
||||
.ser_opts.parity = UART_PARITY_DISABLE,
|
||||
.ser_opts.response_tout_ms = 1,
|
||||
.ser_opts.test_tout_us = TEST_SLAVE_SEND_TOUT_US
|
||||
};
|
||||
|
||||
TEST_ASSERT_NOT_NULL(test_common_slave_serial_create(&slave_config1, 0));
|
||||
|
||||
mb_communication_info_t slave_config2 = {
|
||||
.ser_opts.port = TEST_SER_PORT_NUM1,
|
||||
.ser_opts.mode = MB_RTU,
|
||||
.ser_opts.uid = MB_DEVICE_ADDR2,
|
||||
.ser_opts.data_bits = UART_DATA_8_BITS,
|
||||
.ser_opts.stop_bits = UART_STOP_BITS_2,
|
||||
.ser_opts.baudrate = 115200,
|
||||
.ser_opts.parity = UART_PARITY_DISABLE,
|
||||
.ser_opts.response_tout_ms = 1,
|
||||
.ser_opts.test_tout_us = TEST_SLAVE_SEND_TOUT_US
|
||||
};
|
||||
|
||||
TEST_ASSERT_NOT_NULL(test_common_slave_serial_create(&slave_config2, 0));
|
||||
|
||||
// Initialize and start Modbus controller
|
||||
mb_communication_info_t master_config = {
|
||||
.ser_opts.port = TEST_SER_PORT_NUM1,
|
||||
.ser_opts.mode = MB_RTU,
|
||||
.ser_opts.data_bits = UART_DATA_8_BITS,
|
||||
.ser_opts.stop_bits = UART_STOP_BITS_2,
|
||||
.ser_opts.baudrate = 115200,
|
||||
.ser_opts.parity = UART_PARITY_DISABLE,
|
||||
.ser_opts.response_tout_ms = TEST_MASTER_RESPOND_TOUT_MS,
|
||||
.ser_opts.test_tout_us = TEST_MASTER_SEND_TOUT_US
|
||||
};
|
||||
|
||||
TEST_ASSERT_NOT_NULL(test_common_master_serial_create(&master_config, 0, &descriptors[0], num_descriptors));
|
||||
}
|
||||
|
||||
TEST(modbus_adapter_serial, test_modbus_adapter_ascii)
|
||||
{
|
||||
mb_communication_info_t slave_config1 = {
|
||||
.ser_opts.port = TEST_SER_PORT_NUM1,
|
||||
.ser_opts.mode = MB_ASCII,
|
||||
.ser_opts.uid = MB_DEVICE_ADDR1,
|
||||
.ser_opts.data_bits = UART_DATA_8_BITS,
|
||||
.ser_opts.stop_bits = UART_STOP_BITS_2,
|
||||
.ser_opts.baudrate = 115200,
|
||||
.ser_opts.parity = UART_PARITY_DISABLE,
|
||||
.ser_opts.response_tout_ms = 1,
|
||||
.ser_opts.test_tout_us = TEST_SLAVE_SEND_TOUT_US
|
||||
};
|
||||
|
||||
TEST_ASSERT_NOT_NULL(test_common_slave_serial_create(&slave_config1, 0));
|
||||
|
||||
mb_communication_info_t slave_config2 = {
|
||||
.ser_opts.port = TEST_SER_PORT_NUM1,
|
||||
.ser_opts.mode = MB_ASCII,
|
||||
.ser_opts.uid = MB_DEVICE_ADDR2,
|
||||
.ser_opts.data_bits = UART_DATA_8_BITS,
|
||||
.ser_opts.stop_bits = UART_STOP_BITS_2,
|
||||
.ser_opts.baudrate = 115200,
|
||||
.ser_opts.parity = UART_PARITY_DISABLE,
|
||||
.ser_opts.response_tout_ms = 1,
|
||||
.ser_opts.test_tout_us = TEST_SLAVE_SEND_TOUT_US
|
||||
};
|
||||
|
||||
TEST_ASSERT_NOT_NULL(test_common_slave_serial_create(&slave_config2, 0));
|
||||
|
||||
mb_communication_info_t master_config = {
|
||||
.ser_opts.port = TEST_SER_PORT_NUM1,
|
||||
.ser_opts.mode = MB_ASCII,
|
||||
.ser_opts.data_bits = UART_DATA_8_BITS,
|
||||
.ser_opts.stop_bits = UART_STOP_BITS_2,
|
||||
.ser_opts.baudrate = 115200,
|
||||
.ser_opts.parity = UART_PARITY_DISABLE,
|
||||
.ser_opts.response_tout_ms = TEST_MASTER_RESPOND_TOUT_MS,
|
||||
.ser_opts.test_tout_us = TEST_MASTER_SEND_TOUT_US
|
||||
};
|
||||
|
||||
TEST_ASSERT_NOT_NULL(test_common_master_serial_create(&master_config, 0, &descriptors[0], num_descriptors));
|
||||
}
|
||||
|
||||
TEST(modbus_adapter_serial, test_modbus_adapter_rtu_two_ports)
|
||||
{
|
||||
mb_communication_info_t slave_config1 = {
|
||||
.ser_opts.port = TEST_SER_PORT_NUM1,
|
||||
.ser_opts.mode = MB_ASCII,
|
||||
.ser_opts.uid = MB_DEVICE_ADDR1,
|
||||
.ser_opts.data_bits = UART_DATA_8_BITS,
|
||||
.ser_opts.stop_bits = UART_STOP_BITS_2,
|
||||
.ser_opts.baudrate = 115200,
|
||||
.ser_opts.parity = UART_PARITY_DISABLE,
|
||||
.ser_opts.response_tout_ms = 1,
|
||||
.ser_opts.test_tout_us = TEST_SLAVE_SEND_TOUT_US
|
||||
};
|
||||
|
||||
TEST_ASSERT_NOT_NULL(test_common_slave_serial_create(&slave_config1, 0));
|
||||
|
||||
mb_communication_info_t slave_config2 = {
|
||||
.ser_opts.port = TEST_SER_PORT_NUM1,
|
||||
.ser_opts.mode = MB_ASCII,
|
||||
.ser_opts.uid = MB_DEVICE_ADDR2,
|
||||
.ser_opts.data_bits = UART_DATA_8_BITS,
|
||||
.ser_opts.stop_bits = UART_STOP_BITS_2,
|
||||
.ser_opts.baudrate = 115200,
|
||||
.ser_opts.parity = UART_PARITY_DISABLE,
|
||||
.ser_opts.response_tout_ms = 1,
|
||||
.ser_opts.test_tout_us = TEST_SLAVE_SEND_TOUT_US
|
||||
};
|
||||
|
||||
TEST_ASSERT_NOT_NULL(test_common_slave_serial_create(&slave_config2, 0));
|
||||
|
||||
mb_communication_info_t slave_config3 = {
|
||||
.ser_opts.port = TEST_SER_PORT_NUM2,
|
||||
.ser_opts.mode = MB_RTU,
|
||||
.ser_opts.uid = MB_DEVICE_ADDR1,
|
||||
.ser_opts.data_bits = UART_DATA_8_BITS,
|
||||
.ser_opts.stop_bits = UART_STOP_BITS_2,
|
||||
.ser_opts.baudrate = 115200,
|
||||
.ser_opts.parity = UART_PARITY_DISABLE,
|
||||
.ser_opts.response_tout_ms = 1,
|
||||
.ser_opts.test_tout_us = TEST_SLAVE_SEND_TOUT_US
|
||||
};
|
||||
|
||||
TEST_ASSERT_NOT_NULL(test_common_slave_serial_create(&slave_config3, 0));
|
||||
|
||||
mb_communication_info_t slave_config4 = {
|
||||
.ser_opts.port = TEST_SER_PORT_NUM2,
|
||||
.ser_opts.mode = MB_RTU,
|
||||
.ser_opts.uid = MB_DEVICE_ADDR2,
|
||||
.ser_opts.data_bits = UART_DATA_8_BITS,
|
||||
.ser_opts.stop_bits = UART_STOP_BITS_2,
|
||||
.ser_opts.baudrate = 115200,
|
||||
.ser_opts.parity = UART_PARITY_DISABLE,
|
||||
.ser_opts.response_tout_ms = 1,
|
||||
.ser_opts.test_tout_us = TEST_SLAVE_SEND_TOUT_US
|
||||
};
|
||||
|
||||
TEST_ASSERT_NOT_NULL(test_common_slave_serial_create(&slave_config4, 0));
|
||||
|
||||
mb_communication_info_t master_config1 = {
|
||||
.ser_opts.port = TEST_SER_PORT_NUM1,
|
||||
.ser_opts.mode = MB_ASCII,
|
||||
.ser_opts.data_bits = UART_DATA_8_BITS,
|
||||
.ser_opts.stop_bits = UART_STOP_BITS_2,
|
||||
.ser_opts.baudrate = 115200,
|
||||
.ser_opts.parity = UART_PARITY_DISABLE,
|
||||
.ser_opts.response_tout_ms = TEST_MASTER_RESPOND_TOUT_MS,
|
||||
.ser_opts.test_tout_us = TEST_SLAVE_SEND_TOUT_US
|
||||
};
|
||||
|
||||
TEST_ASSERT_NOT_NULL(test_common_master_serial_create(&master_config1, 0, &descriptors[0], num_descriptors));
|
||||
|
||||
mb_communication_info_t master_config2 = {
|
||||
.ser_opts.port = TEST_SER_PORT_NUM2,
|
||||
.ser_opts.mode = MB_RTU,
|
||||
.ser_opts.data_bits = UART_DATA_8_BITS,
|
||||
.ser_opts.stop_bits = UART_STOP_BITS_2,
|
||||
.ser_opts.baudrate = 115200,
|
||||
.ser_opts.parity = UART_PARITY_DISABLE,
|
||||
.ser_opts.response_tout_ms = TEST_MASTER_RESPOND_TOUT_MS,
|
||||
.ser_opts.test_tout_us = TEST_SLAVE_SEND_TOUT_US
|
||||
};
|
||||
|
||||
TEST_ASSERT_NOT_NULL(test_common_master_serial_create(&master_config2, 0, &descriptors[0], num_descriptors));
|
||||
}
|
||||
|
||||
TEST_GROUP_RUNNER(modbus_adapter_serial)
|
||||
{
|
||||
RUN_TEST_CASE(modbus_adapter_serial, test_modbus_adapter_rtu);
|
||||
RUN_TEST_CASE(modbus_adapter_serial, test_modbus_adapter_ascii);
|
||||
RUN_TEST_CASE(modbus_adapter_serial, test_modbus_adapter_rtu_two_ports);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
128
test_apps/adapter_tests/main/test_modbus_adapter_tcp.c
Normal file
128
test_apps/adapter_tests/main/test_modbus_adapter_tcp.c
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include "unity_fixture.h"
|
||||
|
||||
#include "test_utils.h"
|
||||
|
||||
#if __has_include("unity_test_utils.h")
|
||||
#define UNITY_TEST_UTILS_INCLUDED
|
||||
// unity test utils are used
|
||||
#include "unity_test_utils.h"
|
||||
#include "unity_test_utils_memory.h"
|
||||
#else
|
||||
// Unit_test_app utils from test_utils ("test_utils.h"), v4.4
|
||||
#define unity_utils_task_delete test_utils_task_delete
|
||||
#endif
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "test_common.h"
|
||||
|
||||
#define TEST_TCP_PORT_NUM (1502)
|
||||
#define TEST_TASK_TIMEOUT_MS (120000)
|
||||
#define TEST_LEAK_WARN (32)
|
||||
#define TEST_LEAK_CRITICAL (64)
|
||||
#define TEST_SLAVE_SEND_TOUT_US (50)
|
||||
#define TEST_MASTER_SEND_TOUT_US (50)
|
||||
|
||||
#define TEST_MASTER_RESPOND_TOUT_MS (CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND)
|
||||
|
||||
#define TAG "MODBUS_TCP_TEST"
|
||||
|
||||
// The workaround to statically link whole test library
|
||||
__attribute__((unused)) bool mb_test_include_adapter_impl_tcp = true;
|
||||
|
||||
#if (CONFIG_FMB_COMM_MODE_TCP_EN)
|
||||
|
||||
// Example Data (Object) Dictionary for Modbus parameters
|
||||
static const mb_parameter_descriptor_t descriptors[] = {
|
||||
{CID_DEV_REG0, STR("MB_hold_reg-0"), STR("Data"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0, 1,
|
||||
0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE_TRIGGER},
|
||||
{CID_DEV_REG1, STR("MB_hold_reg-1"), STR("Data"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 1, 1,
|
||||
0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE_TRIGGER},
|
||||
{CID_DEV_REG2, STR("MB_hold_reg-2"), STR("Data"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 2, 1,
|
||||
0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE_TRIGGER},
|
||||
{CID_DEV_REG3, STR("MB_hold_reg-3"), STR("Data"), MB_DEVICE_ADDR2, MB_PARAM_HOLDING, 3, 1,
|
||||
0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE_TRIGGER},
|
||||
{CID_DEV_REG_COUNT, STR("CYCLE_COUNTER"), STR("Data"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 4, 1,
|
||||
0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE_TRIGGER}
|
||||
};
|
||||
|
||||
// Calculate number of parameters in the table
|
||||
const uint16_t num_descriptors = (sizeof(descriptors) / sizeof(descriptors[0]));
|
||||
|
||||
TEST_GROUP(modbus_adapter_tcp);
|
||||
|
||||
TEST_SETUP(modbus_adapter_tcp)
|
||||
{
|
||||
test_common_start();
|
||||
}
|
||||
|
||||
TEST_TEAR_DOWN(modbus_adapter_tcp)
|
||||
{
|
||||
int task_count = test_common_task_start_all(1);
|
||||
TEST_ASSERT_TRUE(task_count > 0);
|
||||
TEST_ASSERT_EQUAL(task_count, test_common_task_wait_done_delete_all(TEST_TASK_TIMEOUT_MS));
|
||||
test_common_stop();
|
||||
ESP_LOGI(TAG, "%s, done successfully.", __func__);
|
||||
}
|
||||
|
||||
const char *slave_tcp_addr_table[] = {
|
||||
"01;mb_slave_tcp_01;1502", // Corresponds to characteristic MB_DEVICE_ADDR1 "mb_slave_tcp_01"
|
||||
"200;mb_slave_tcp_c8;1502", // Corresponds to characteristic MB_DEVICE_ADDR2 "mb_slave_tcp_C8"
|
||||
NULL // End of table condition (must be included)
|
||||
};
|
||||
|
||||
TEST(modbus_adapter_tcp, test_modbus_adapter_tcp)
|
||||
{
|
||||
mb_communication_info_t tcp_slave_cfg_1 = {
|
||||
.tcp_opts.port = TEST_TCP_PORT_NUM,
|
||||
.tcp_opts.mode = MB_TCP,
|
||||
.tcp_opts.addr_type = MB_IPV4,
|
||||
.tcp_opts.ip_addr_table = NULL,
|
||||
.tcp_opts.uid = MB_DEVICE_ADDR1,
|
||||
.tcp_opts.start_disconnected = true,
|
||||
.tcp_opts.response_tout_ms = 1,
|
||||
.tcp_opts.test_tout_us = TEST_SLAVE_SEND_TOUT_US
|
||||
};
|
||||
|
||||
TEST_ASSERT_NOT_NULL(test_common_slave_tcp_create(&tcp_slave_cfg_1, 0));
|
||||
|
||||
mb_communication_info_t tcp_slave_cfg_2 = {
|
||||
.tcp_opts.port = TEST_TCP_PORT_NUM,
|
||||
.tcp_opts.mode = MB_TCP,
|
||||
.tcp_opts.addr_type = MB_IPV4,
|
||||
.tcp_opts.ip_addr_table = NULL,
|
||||
.tcp_opts.uid = MB_DEVICE_ADDR2,
|
||||
.tcp_opts.start_disconnected = true,
|
||||
.tcp_opts.response_tout_ms = TEST_MASTER_RESPOND_TOUT_MS,
|
||||
.tcp_opts.test_tout_us = TEST_SLAVE_SEND_TOUT_US
|
||||
};
|
||||
|
||||
TEST_ASSERT_NOT_NULL(test_common_slave_tcp_create(&tcp_slave_cfg_2, 0));
|
||||
|
||||
// Initialize and start Modbus controller
|
||||
mb_communication_info_t tcp_master_cfg_1 = {
|
||||
.tcp_opts.port = TEST_TCP_PORT_NUM,
|
||||
.tcp_opts.mode = MB_TCP,
|
||||
.tcp_opts.addr_type = MB_IPV4,
|
||||
.tcp_opts.ip_addr_table = (void *)slave_tcp_addr_table,
|
||||
.tcp_opts.uid = 0,
|
||||
.tcp_opts.start_disconnected = false,
|
||||
.tcp_opts.response_tout_ms = TEST_MASTER_RESPOND_TOUT_MS,
|
||||
.tcp_opts.test_tout_us = TEST_MASTER_SEND_TOUT_US
|
||||
};
|
||||
|
||||
TEST_ASSERT_NOT_NULL(test_common_master_tcp_create(&tcp_master_cfg_1, 0, &descriptors[0], num_descriptors));
|
||||
}
|
||||
|
||||
TEST_GROUP_RUNNER(modbus_adapter_tcp)
|
||||
{
|
||||
RUN_TEST_CASE(modbus_adapter_tcp, test_modbus_adapter_tcp);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -6,12 +6,12 @@ from pytest_embedded import Dut
|
||||
|
||||
|
||||
CONFIGS = [
|
||||
pytest.param('serial', marks=[pytest.mark.esp32, pytest.mark.esp32s2, pytest.mark.esp32s3, pytest.mark.esp32c3]),
|
||||
pytest.param('tcp', marks=[pytest.mark.esp32, pytest.mark.esp32s2, pytest.mark.esp32s3, pytest.mark.esp32c3]),
|
||||
pytest.param('serial', marks=[pytest.mark.esp32, pytest.mark.esp32p4]),
|
||||
pytest.param('tcp', marks=[pytest.mark.esp32, pytest.mark.esp32p4]),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.generic_multi_device
|
||||
@pytest.mark.temp_skip_ci(targets=['esp32p4'], reason='no multi-dev runner')
|
||||
@pytest.mark.multi_dut_modbus_generic
|
||||
@pytest.mark.parametrize('config', CONFIGS, indirect=True)
|
||||
def test_modbus_comm_adapter(dut: Dut) -> None:
|
||||
dut.expect_unity_test_output()
|
||||
|
@ -10,7 +10,7 @@ CONFIG_FMB_PORT_TASK_PRIO=10
|
||||
CONFIG_FMB_COMM_MODE_RTU_EN=y
|
||||
CONFIG_FMB_COMM_MODE_ASCII_EN=y
|
||||
CONFIG_FMB_COMM_MODE_TCP_EN=n
|
||||
CONFIG_FMB_TCP_UID_ENABLED=y
|
||||
CONFIG_FMB_TCP_UID_ENABLED=n
|
||||
CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=2000
|
||||
CONFIG_FMB_MASTER_DELAY_MS_CONVERT=300
|
||||
CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y
|
||||
|
@ -1,12 +1,14 @@
|
||||
set(srcs "test_app_main.c"
|
||||
"test_modbus_rs485_comm_master_slave.c"
|
||||
"test_modbus_tcp_comm_master_slave.c"
|
||||
)
|
||||
|
||||
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
|
||||
idf_component_register(SRCS ${srcs}
|
||||
PRIV_REQUIRES cmock test_utils test_common unity
|
||||
PRIV_REQUIRES cmock test_utils test_common unity nvs_flash esp_event esp_eth
|
||||
)
|
||||
|
||||
# The workaround for WHOLE_ARCHIVE is absent in v4.4
|
||||
set_property(TARGET ${COMPONENT_LIB} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-u mb_test_include_impl")
|
||||
# The workaround for WHOLE_ARCHIVE which is absent in v4.4
|
||||
set_property(TARGET ${COMPONENT_LIB} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-u mb_test_include_phys_impl_tcp")
|
||||
set_property(TARGET ${COMPONENT_LIB} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-u mb_test_include_phys_impl_serial")
|
||||
|
||||
|
@ -1,6 +1,12 @@
|
||||
dependencies:
|
||||
idf: ">=4.3"
|
||||
espressif/esp-modbus:
|
||||
version: "^2.0"
|
||||
version: "^2.0.0-beta"
|
||||
override_path: "../../../"
|
||||
espressif/mdns:
|
||||
version: "^1.0.0"
|
||||
rules:
|
||||
- if: "idf_version >=5.0"
|
||||
protocol_examples_common:
|
||||
path: ${IDF_PATH}/examples/common_components/protocol_examples_common
|
||||
|
||||
|
@ -7,16 +7,6 @@
|
||||
#include "unity.h"
|
||||
#include "test_common.h"
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
test_common_start();
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
test_common_stop();
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
printf("Modbus RS485 multi-device test cases/n");
|
||||
|
@ -17,28 +17,27 @@
|
||||
#define unity_utils_task_delete test_utils_task_delete
|
||||
#endif
|
||||
|
||||
#define TEST_SER_PORT_NUM (1)
|
||||
#define TEST_TASK_TIMEOUT_MS (30000)
|
||||
#define TEST_SEND_TOUT_US (30000)
|
||||
#define TEST_RESP_TOUT_MS (1000)
|
||||
#define TEST_SER_PORT_NUM (1)
|
||||
#define TEST_TASK_TIMEOUT_MS (160000)
|
||||
#define TEST_SEND_TOUT_US (30000)
|
||||
#define TEST_RESP_TOUT_MS (1000)
|
||||
#define TEST_BAUD_RATE (115200)
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#define TEST_SER_PIN_RX (22)
|
||||
#define TEST_SER_PIN_TX (23)
|
||||
#define TEST_SER_PIN_RX (22)
|
||||
#define TEST_SER_PIN_TX (23)
|
||||
// RTS for RS485 Half-Duplex Mode manages DE/~RE
|
||||
#define TEST_SER_PIN_RTS (18)
|
||||
#define TEST_BAUD_RATE (115200)
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
#define TEST_SER_PIN_RX (4)
|
||||
#define TEST_SER_PIN_TX (5)
|
||||
#define TEST_SER_PIN_RTS (10)
|
||||
#define TEST_BAUD_RATE (115200)
|
||||
#define TEST_SER_PIN_RTS (18)
|
||||
#else
|
||||
#define TEST_SER_PIN_RX (4)
|
||||
#define TEST_SER_PIN_TX (5)
|
||||
#define TEST_SER_PIN_RTS (10)
|
||||
#endif
|
||||
|
||||
#define TEST_MASTER_RESPOND_TOUT_MS CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND
|
||||
#define TEST_MASTER_RESPOND_TOUT_MS (CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND)
|
||||
|
||||
// The workaround to statically link the whole test library
|
||||
__attribute__((unused)) bool mb_test_include_impl = 1;
|
||||
__attribute__((unused)) bool mb_test_include_phys_impl_serial = true;
|
||||
|
||||
#define TAG "MODBUS_SERIAL_COMM_TEST"
|
||||
|
||||
@ -61,32 +60,6 @@ static const mb_parameter_descriptor_t descriptors[] = {
|
||||
// The number of parameters in the table
|
||||
const uint16_t num_descriptors = (sizeof(descriptors) / sizeof(descriptors[0]));
|
||||
|
||||
static void test_task_start_wait_done(TaskHandle_t task_handle)
|
||||
{
|
||||
uint32_t test_task = 0;
|
||||
|
||||
// Start test sequence intentionally in the task
|
||||
test_common_task_notify_start(task_handle, 1);
|
||||
|
||||
for (int i = 0; (i < 2); i++) {
|
||||
test_task = test_common_wait_done(pdMS_TO_TICKS(TEST_TASK_TIMEOUT_MS));
|
||||
if (test_task == (uint32_t)task_handle) {
|
||||
unity_utils_task_delete((TaskHandle_t)test_task);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
vTaskDelay(5); // A small delay to let the test lower priority task delete itself
|
||||
if (test_task != (uint32_t)task_handle) {
|
||||
ESP_LOGI(TAG, "Could not complete task 0x%" PRIx32" after %d ms, force kill the task.",
|
||||
(uint32_t)task_handle, TEST_TASK_TIMEOUT_MS);
|
||||
unity_utils_task_delete((TaskHandle_t)task_handle);
|
||||
}
|
||||
|
||||
TEST_ASSERT_EQUAL(test_task, (uint32_t)task_handle);
|
||||
ESP_LOGI(TAG, "Test task 0x%" PRIx32 ", done successfully.", (uint32_t)task_handle);
|
||||
}
|
||||
|
||||
static void test_modbus_rs485_rtu_slave(void)
|
||||
{
|
||||
mb_communication_info_t slave_config1 = {
|
||||
@ -104,7 +77,7 @@ static void test_modbus_rs485_rtu_slave(void)
|
||||
TEST_ESP_OK(uart_set_pin(slave_config1.ser_opts.port, TEST_SER_PIN_TX,
|
||||
TEST_SER_PIN_RX, TEST_SER_PIN_RTS, UART_PIN_NO_CHANGE));
|
||||
|
||||
TaskHandle_t slave_task_handle = test_slave_serial_create(&slave_config1);
|
||||
TaskHandle_t slave_task_handle = test_common_slave_serial_create(&slave_config1, 0);
|
||||
|
||||
// Set driver mode to Half Duplex
|
||||
TEST_ESP_OK(uart_set_mode(slave_config1.ser_opts.port, UART_MODE_RS485_HALF_DUPLEX));
|
||||
@ -114,8 +87,8 @@ static void test_modbus_rs485_rtu_slave(void)
|
||||
unity_send_signal("Slave_ready");
|
||||
unity_wait_for_signal("Master_started");
|
||||
|
||||
test_task_start_wait_done(slave_task_handle);
|
||||
|
||||
test_common_task_start(slave_task_handle, 1);
|
||||
TEST_ASSERT_TRUE(test_common_task_wait_done(slave_task_handle, pdMS_TO_TICKS(TEST_TASK_TIMEOUT_MS)));
|
||||
}
|
||||
|
||||
static void test_modbus_rs485_rtu_master(void)
|
||||
@ -136,14 +109,15 @@ static void test_modbus_rs485_rtu_master(void)
|
||||
.ser_opts.test_tout_us = TEST_SEND_TOUT_US
|
||||
};
|
||||
|
||||
TaskHandle_t master_task_handle = test_master_serial_create(&master_config, &descriptors[0], num_descriptors);
|
||||
TaskHandle_t master_task_handle = test_common_master_serial_create(&master_config, 0, &descriptors[0], num_descriptors);
|
||||
|
||||
// Set driver mode to Half Duplex
|
||||
TEST_ESP_OK(uart_set_mode(master_config.ser_opts.port, UART_MODE_RS485_HALF_DUPLEX));
|
||||
TEST_ESP_OK(uart_set_pin(master_config.ser_opts.port, TEST_SER_PIN_TX,
|
||||
TEST_SER_PIN_RX, TEST_SER_PIN_RTS, UART_PIN_NO_CHANGE));
|
||||
|
||||
test_task_start_wait_done(master_task_handle);
|
||||
test_common_task_start(master_task_handle, 1);
|
||||
TEST_ASSERT_TRUE(test_common_task_wait_done(master_task_handle, pdMS_TO_TICKS(TEST_TASK_TIMEOUT_MS)));
|
||||
}
|
||||
|
||||
/*
|
||||
@ -168,7 +142,7 @@ static void test_modbus_rs485_ascii_slave(void)
|
||||
TEST_ESP_OK(uart_set_pin(slave_config1.ser_opts.port, TEST_SER_PIN_TX,
|
||||
TEST_SER_PIN_RX, TEST_SER_PIN_RTS, UART_PIN_NO_CHANGE));
|
||||
|
||||
TaskHandle_t slave_task_handle = test_slave_serial_create(&slave_config1);
|
||||
TaskHandle_t slave_task_handle = test_common_slave_serial_create(&slave_config1, 0);
|
||||
|
||||
// Set driver mode to Half Duplex
|
||||
TEST_ESP_OK(uart_set_mode(slave_config1.ser_opts.port, UART_MODE_RS485_HALF_DUPLEX));
|
||||
@ -178,9 +152,9 @@ static void test_modbus_rs485_ascii_slave(void)
|
||||
unity_send_signal("Slave_ready");
|
||||
unity_wait_for_signal("Master_started");
|
||||
|
||||
test_task_start_wait_done(slave_task_handle);
|
||||
|
||||
}
|
||||
test_common_task_start(slave_task_handle, 1);
|
||||
TEST_ASSERT_TRUE(test_common_task_wait_done(slave_task_handle, pdMS_TO_TICKS(TEST_TASK_TIMEOUT_MS)));
|
||||
};
|
||||
|
||||
static void test_modbus_rs485_ascii_master(void)
|
||||
{
|
||||
@ -199,16 +173,16 @@ static void test_modbus_rs485_ascii_master(void)
|
||||
.ser_opts.test_tout_us = TEST_SEND_TOUT_US
|
||||
};
|
||||
|
||||
TaskHandle_t master_task_handle = test_master_serial_create(&master_config, &descriptors[0], num_descriptors);
|
||||
TaskHandle_t master_task_handle = test_common_master_serial_create(&master_config, 0, &descriptors[0], num_descriptors);
|
||||
|
||||
// Set driver mode to Half Duplex
|
||||
TEST_ESP_OK(uart_set_mode(master_config.ser_opts.port, UART_MODE_RS485_HALF_DUPLEX));
|
||||
TEST_ESP_OK(uart_set_pin(master_config.ser_opts.port, TEST_SER_PIN_TX,
|
||||
TEST_SER_PIN_RX, TEST_SER_PIN_RTS, UART_PIN_NO_CHANGE));
|
||||
|
||||
unity_send_signal("Master_started");
|
||||
|
||||
test_task_start_wait_done(master_task_handle);
|
||||
test_common_task_start(master_task_handle, 1);
|
||||
TEST_ASSERT_TRUE(test_common_task_wait_done(master_task_handle, pdMS_TO_TICKS(TEST_TASK_TIMEOUT_MS)));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -0,0 +1,241 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include "unity.h"
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "test_common.h"
|
||||
#include "test_utils.h"
|
||||
|
||||
#include "nvs_flash.h"
|
||||
|
||||
#if MB_MDNS_IS_INCLUDED
|
||||
#include "mdns.h"
|
||||
#endif
|
||||
|
||||
#include "protocol_examples_common.h"
|
||||
#include "esp_event.h"
|
||||
|
||||
#if __has_include("unity_test_utils.h")
|
||||
// unity test utils are used
|
||||
#include "unity_test_utils.h"
|
||||
#else
|
||||
// Unit_test_app utils from test_utils ("test_utils.h"), v4.4
|
||||
#define unity_utils_task_delete test_utils_task_delete
|
||||
#endif
|
||||
|
||||
#define TEST_TCP_PORT_NUM1 (1502)
|
||||
#define TEST_TCP_PORT_NUM2 (502)
|
||||
#define TEST_TCP_TASK_TIMEOUT_MS (160000)
|
||||
#define TEST_TCP_SLAVE_SEND_TOUT_US (500)
|
||||
#define TEST_TCP_MASTER_SEND_TOUT_US (500)
|
||||
|
||||
#define TEST_MASTER_RESPOND_TOUT_MS (CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND)
|
||||
|
||||
// The workaround to statically link the whole test library
|
||||
__attribute__((unused)) bool mb_test_include_phys_impl_tcp = true;
|
||||
|
||||
#define TAG "MODBUS_TCP_COMM_TEST"
|
||||
|
||||
#if (CONFIG_FMB_COMM_MODE_TCP_EN)
|
||||
|
||||
// Example Data (Object) Dictionary for Modbus parameters
|
||||
static const mb_parameter_descriptor_t descriptors[] = {
|
||||
{CID_DEV_REG0, STR("MB_hold_reg-0"), STR("Data"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0, 1,
|
||||
0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE_TRIGGER},
|
||||
{CID_DEV_REG1, STR("MB_hold_reg-1"), STR("Data"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 1, 1,
|
||||
0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE_TRIGGER},
|
||||
{CID_DEV_REG2, STR("MB_hold_reg-2"), STR("Data"), MB_DEVICE_ADDR2, MB_PARAM_HOLDING, 2, 1,
|
||||
0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE_TRIGGER},
|
||||
{CID_DEV_REG3, STR("MB_hold_reg-3"), STR("Data"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 3, 1,
|
||||
0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE_TRIGGER},
|
||||
{CID_DEV_REG_COUNT, STR("CYCLE_COUNTER"), STR("Data"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 4, 1,
|
||||
0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE_TRIGGER}
|
||||
};
|
||||
|
||||
// The number of parameters in the table
|
||||
const uint16_t num_descriptors = (sizeof(descriptors) / sizeof(descriptors[0]));
|
||||
|
||||
const char *slave_tcp_addr_table[] = {
|
||||
"01;mb_slave_tcp_01;1502", // Corresponds to characteristic MB_DEVICE_ADDR1
|
||||
"200;mb_slave_tcp_c8;502", // Corresponds to characteristic MB_DEVICE_ADDR2
|
||||
NULL // End of table condition (must be included)
|
||||
};
|
||||
|
||||
static esp_err_t test_tcp_services_init(void **pnetif)
|
||||
{
|
||||
esp_err_t result = nvs_flash_init();
|
||||
if ((result == ESP_ERR_NVS_NO_FREE_PAGES) || (result == ESP_ERR_NVS_NEW_VERSION_FOUND)) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
result = nvs_flash_init();
|
||||
}
|
||||
ESP_RETURN_ON_FALSE((result == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
TAG,
|
||||
"nvs_flash_init fail, returns(0x%x).",
|
||||
(int)result);
|
||||
result = esp_netif_init();
|
||||
ESP_RETURN_ON_FALSE((result == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
TAG,
|
||||
"esp_netif_init fail, returns(0x%x).",
|
||||
(int)result);
|
||||
result = esp_event_loop_create_default();
|
||||
ESP_RETURN_ON_FALSE((result == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
TAG,
|
||||
"esp_event_loop_create_default fail, returns(0x%x).",
|
||||
(int)result);
|
||||
#if MB_MDNS_IS_INCLUDED
|
||||
// Start mdns service and register device
|
||||
if (mdns_init() != ESP_OK) {
|
||||
ESP_LOGE(TAG, "initialization of mdns fail.");
|
||||
};
|
||||
#endif
|
||||
// This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
|
||||
// Read "Establishing Wi-Fi or Ethernet Connection" section in
|
||||
// examples/protocols/README.md for more information about this function.
|
||||
result = example_connect();
|
||||
ESP_RETURN_ON_FALSE((result == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
TAG,
|
||||
"example_connect fail, returns(0x%x).",
|
||||
(int)result);
|
||||
#if CONFIG_EXAMPLE_CONNECT_WIFI
|
||||
// result = esp_wifi_set_ps(WIFI_PS_NONE);
|
||||
// ESP_RETURN_ON_FALSE((result == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
// TAG,
|
||||
// "esp_wifi_set_ps fail, returns(0x%x).",
|
||||
// (int)result);
|
||||
#endif
|
||||
if (pnetif) {
|
||||
*pnetif = get_example_netif();
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t test_tcp_services_destroy(void)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
|
||||
err = example_disconnect();
|
||||
ESP_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
TAG,
|
||||
"example_disconnect fail, returns(0x%x).",
|
||||
(int)err);
|
||||
err = esp_event_loop_delete_default();
|
||||
ESP_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
TAG,
|
||||
"esp_event_loop_delete_default fail, returns(0x%x).",
|
||||
(int)err);
|
||||
err = esp_netif_deinit();
|
||||
ESP_RETURN_ON_FALSE(((err == ESP_OK) || (err == ESP_ERR_NOT_SUPPORTED)),
|
||||
ESP_ERR_INVALID_STATE,
|
||||
TAG,
|
||||
"esp_netif_deinit fail, returns(0x%x).",
|
||||
(int)err);
|
||||
err = nvs_flash_deinit();
|
||||
ESP_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
TAG,
|
||||
"nvs_flash_deinit fail, returns(0x%x).",
|
||||
(int)err);
|
||||
#if MB_MDNS_IS_INCLUDED
|
||||
// Stop mdns service and register device
|
||||
mdns_free();
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
|
||||
static void test_modbus_tcp_slave(void)
|
||||
{
|
||||
void *pnetif = NULL;
|
||||
TEST_ASSERT_TRUE(test_tcp_services_init(&pnetif) == ESP_OK);
|
||||
TEST_ASSERT_NOT_NULL(pnetif);
|
||||
test_common_start();
|
||||
|
||||
mb_communication_info_t tcp_slave_cfg_1 = {
|
||||
.tcp_opts.port = TEST_TCP_PORT_NUM1,
|
||||
.tcp_opts.mode = MB_TCP,
|
||||
.tcp_opts.addr_type = MB_IPV4,
|
||||
.tcp_opts.ip_addr_table = NULL,
|
||||
.tcp_opts.uid = MB_DEVICE_ADDR1,
|
||||
.tcp_opts.start_disconnected = true,
|
||||
.tcp_opts.response_tout_ms = 1,
|
||||
.tcp_opts.test_tout_us = TEST_TCP_SLAVE_SEND_TOUT_US,
|
||||
.tcp_opts.ip_netif_ptr = pnetif
|
||||
};
|
||||
|
||||
TEST_ASSERT_NOT_NULL(test_common_slave_tcp_create(&tcp_slave_cfg_1, 0));
|
||||
|
||||
ESP_LOGI(TAG, "Slave TCP #1 is started. (%s).", __func__);
|
||||
|
||||
mb_communication_info_t tcp_slave_cfg_2 = {
|
||||
.tcp_opts.port = TEST_TCP_PORT_NUM2,
|
||||
.tcp_opts.mode = MB_TCP,
|
||||
.tcp_opts.addr_type = MB_IPV4,
|
||||
.tcp_opts.ip_addr_table = NULL,
|
||||
.tcp_opts.uid = MB_DEVICE_ADDR2,
|
||||
.tcp_opts.start_disconnected = true,
|
||||
.tcp_opts.response_tout_ms = 1,
|
||||
.tcp_opts.test_tout_us = TEST_TCP_SLAVE_SEND_TOUT_US,
|
||||
.tcp_opts.ip_netif_ptr = pnetif
|
||||
};
|
||||
|
||||
TEST_ASSERT_NOT_NULL(test_common_slave_tcp_create(&tcp_slave_cfg_2, 0));
|
||||
|
||||
ESP_LOGI(TAG, "Slave TCP #2 is started. (%s).", __func__);
|
||||
|
||||
unity_send_signal("Slave_ready");
|
||||
unity_wait_for_signal("Master_started");
|
||||
|
||||
TEST_ASSERT_EQUAL(test_common_task_start_all(1),
|
||||
test_common_task_wait_done_delete_all(TEST_TCP_TASK_TIMEOUT_MS));
|
||||
|
||||
ESP_LOGI(TAG, "Slave TCP test is complited. (%s).", __func__);
|
||||
|
||||
test_common_stop();
|
||||
|
||||
test_tcp_services_destroy();
|
||||
}
|
||||
|
||||
static void test_modbus_tcp_master(void)
|
||||
{
|
||||
void *pnetif = NULL;
|
||||
TEST_ASSERT_TRUE(test_tcp_services_init(&pnetif) == ESP_OK);
|
||||
TEST_ASSERT_NOT_NULL(pnetif);
|
||||
test_common_start();
|
||||
|
||||
ESP_LOGI(TAG, "Master TCP is started (%s).", __func__);
|
||||
unity_wait_for_signal("Slave_ready");
|
||||
|
||||
// Initialize and start Modbus controller
|
||||
mb_communication_info_t tcp_master_cfg_1 = {
|
||||
.tcp_opts.port = TEST_TCP_PORT_NUM1,
|
||||
.tcp_opts.mode = MB_TCP,
|
||||
.tcp_opts.addr_type = MB_IPV4,
|
||||
.tcp_opts.ip_addr_table = (void *)slave_tcp_addr_table,
|
||||
.tcp_opts.uid = 0,
|
||||
.tcp_opts.start_disconnected = false,
|
||||
.tcp_opts.response_tout_ms = TEST_MASTER_RESPOND_TOUT_MS,
|
||||
.tcp_opts.test_tout_us = TEST_TCP_MASTER_SEND_TOUT_US,
|
||||
.tcp_opts.ip_netif_ptr = pnetif
|
||||
};
|
||||
|
||||
TEST_ASSERT_NOT_NULL(test_common_master_tcp_create(&tcp_master_cfg_1, 0, &descriptors[0], num_descriptors));
|
||||
|
||||
unity_send_signal("Master_started");
|
||||
|
||||
TEST_ASSERT_EQUAL(test_common_task_start_all(1),
|
||||
test_common_task_wait_done_delete_all(TEST_TCP_TASK_TIMEOUT_MS));
|
||||
|
||||
test_common_stop();
|
||||
|
||||
test_tcp_services_destroy();
|
||||
ESP_LOGI(TAG, "Master TCP is complited. (%s).", __func__);
|
||||
}
|
||||
|
||||
/*
|
||||
* Modbus TCP multi device test case
|
||||
*/
|
||||
TEST_CASE_MULTIPLE_DEVICES("Modbus TCP multi device master - slave case.", "[modbus][test_env=multi_dut_modbus_tcp]",
|
||||
test_modbus_tcp_slave, test_modbus_tcp_master);
|
||||
|
||||
#endif
|
@ -8,17 +8,16 @@ from pytest_embedded_idf import CaseTester
|
||||
@pytest.mark.multi_dut_modbus_serial
|
||||
@pytest.mark.parametrize('count, config', [(2, 'serial')], indirect=True)
|
||||
def test_modbus_comm_multi_dev_serial(case_tester) -> None: # type: ignore
|
||||
#case_tester.run_all_multi_dev_cases(reset=True)
|
||||
for case in case_tester.test_menu:
|
||||
if case.attributes.get('test_env', 'multi_dut_modbus_serial') == 'multi_dut_modbus_serial':
|
||||
print(case.name)
|
||||
case_tester.run_multi_dev_case(case=case, reset=True)
|
||||
|
||||
|
||||
# @pytest.mark.esp32
|
||||
# @pytest.mark.multi_dut_modbus_tcp
|
||||
# #@pytest.mark.parametrize('count, config', [(2, 'tcp')], indirect=True)
|
||||
# def test_modbus_comm_multi_dev_tcp(case_tester) -> None: # type: ignore
|
||||
# for case in case_tester.test_menu:
|
||||
# if case.attributes.get('test_env', 'multi_dut_modbus_tcp') == 'multi_dut_modbus_tcp':
|
||||
# case_tester.run_multi_dev_case(case=case, reset=True)
|
||||
@pytest.mark.esp32
|
||||
@pytest.mark.multi_dut_modbus_tcp
|
||||
@pytest.mark.parametrize('count, config', [(2, 'wifi'), (2, 'ethernet')], indirect=True)
|
||||
def test_modbus_comm_multi_dev_tcp(case_tester) -> None: # type: ignore
|
||||
for case in case_tester.test_menu:
|
||||
if case.attributes.get('test_env', 'multi_dut_modbus_tcp') == 'multi_dut_modbus_tcp':
|
||||
print(case.name)
|
||||
case_tester.run_multi_dev_case(case=case, reset=True)
|
34
test_apps/physical_tests/sdkconfig.ci.ethernet
Normal file
34
test_apps/physical_tests/sdkconfig.ci.ethernet
Normal file
@ -0,0 +1,34 @@
|
||||
#
|
||||
# Modbus configuration
|
||||
#
|
||||
CONFIG_FMB_COMM_MODE_TCP_EN=y
|
||||
CONFIG_FMB_TCP_PORT_DEFAULT=1502
|
||||
CONFIG_FMB_TCP_CONNECTION_TOUT_SEC=20
|
||||
CONFIG_FMB_PORT_TASK_STACK_SIZE=4096
|
||||
CONFIG_FMB_PORT_TASK_PRIO=10
|
||||
CONFIG_FMB_COMM_MODE_RTU_EN=n
|
||||
CONFIG_FMB_COMM_MODE_ASCII_EN=n
|
||||
CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=3000
|
||||
CONFIG_FMB_MASTER_DELAY_MS_CONVERT=300
|
||||
CONFIG_FMB_TCP_UID_ENABLED=n
|
||||
CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y
|
||||
CONFIG_FMB_EXT_TYPE_SUPPORT=y
|
||||
|
||||
CONFIG_EXAMPLE_CONNECT_IPV6=n
|
||||
CONFIG_EXAMPLE_CONNECT_WIFI=n
|
||||
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
|
||||
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
|
||||
CONFIG_EXAMPLE_ETH_PHY_IP101=y
|
||||
CONFIG_EXAMPLE_ETH_MDC_GPIO=23
|
||||
CONFIG_EXAMPLE_ETH_MDIO_GPIO=18
|
||||
CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5
|
||||
CONFIG_EXAMPLE_ETH_PHY_ADDR=1
|
||||
CONFIG_EXAMPLE_ETHERNET_EMAC_TASK_STACK_SIZE=4096
|
||||
|
||||
CONFIG_ETH_ENABLED=y
|
||||
CONFIG_ETH_USE_ESP32_EMAC=y
|
||||
CONFIG_ETH_PHY_INTERFACE_RMII=y
|
||||
CONFIG_ETH_USE_SPI_ETHERNET=n
|
||||
|
||||
# Enable debug logging
|
||||
CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y
|
@ -9,7 +9,7 @@ CONFIG_FMB_PORT_TASK_STACK_SIZE=4096
|
||||
CONFIG_FMB_PORT_TASK_PRIO=10
|
||||
CONFIG_FMB_COMM_MODE_RTU_EN=y
|
||||
CONFIG_FMB_COMM_MODE_ASCII_EN=y
|
||||
CONFIG_FMB_COMM_MODE_TCP_EN=y
|
||||
CONFIG_FMB_COMM_MODE_TCP_EN=n
|
||||
CONFIG_FMB_TCP_UID_ENABLED=y
|
||||
CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=2000
|
||||
CONFIG_FMB_MASTER_DELAY_MS_CONVERT=300
|
||||
@ -21,3 +21,10 @@ CONFIG_MB_PORT_ADAPTER_EN=n
|
||||
CONFIG_MB_TEST_LEAK_CRITICAL_LEVEL=128
|
||||
CONFIG_MB_TEST_LEAK_WARN_LEVEL=128
|
||||
|
||||
# Avoid CI issues "Warning: The smallest app partition is nearly full (5% free space left)!"
|
||||
CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||
|
||||
# Enable debug logging
|
||||
CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y
|
||||
|
||||
|
24
test_apps/physical_tests/sdkconfig.ci.wifi
Normal file
24
test_apps/physical_tests/sdkconfig.ci.wifi
Normal file
@ -0,0 +1,24 @@
|
||||
CONFIG_FMB_COMM_MODE_TCP_EN=y
|
||||
CONFIG_FMB_TCP_PORT_DEFAULT=502
|
||||
CONFIG_FMB_TCP_CONNECTION_TOUT_SEC=20
|
||||
CONFIG_FMB_PORT_TASK_STACK_SIZE=4096
|
||||
CONFIG_FMB_PORT_TASK_PRIO=10
|
||||
CONFIG_FMB_COMM_MODE_RTU_EN=n
|
||||
CONFIG_FMB_COMM_MODE_ASCII_EN=n
|
||||
CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=3000
|
||||
CONFIG_FMB_MASTER_DELAY_MS_CONVERT=300
|
||||
CONFIG_FMB_TCP_UID_ENABLED=n
|
||||
CONFIG_EXAMPLE_CONNECT_IPV6=n
|
||||
CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y
|
||||
CONFIG_EXAMPLE_CONNECT_ETHERNET=n
|
||||
CONFIG_EXAMPLE_CONNECT_WIFI=y
|
||||
CONFIG_EXAMPLE_WIFI_SSID="${CI_WIFI_SSID}"
|
||||
CONFIG_EXAMPLE_WIFI_PASSWORD="${CI_WIFI_PASSW}"
|
||||
|
||||
|
||||
# Avoid CI issues "Warning: The smallest app partition is nearly full (5% free space left)!"
|
||||
CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||
|
||||
# Enable debug logging
|
||||
CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y
|
@ -18,3 +18,10 @@ CONFIG_MB_PORT_ADAPTER_EN=n
|
||||
CONFIG_MB_TEST_MASTER_TASK_PRIO=5
|
||||
CONFIG_MB_TEST_MASTER_TASK_PRIO=5
|
||||
CONFIG_MB_TEST_COMM_CYCLE_COUNTER=10
|
||||
|
||||
# Avoid CI issues "Warning: The smallest app partition is nearly full (5% free space left)!"
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||
CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y
|
||||
|
||||
# Enable debug logging
|
||||
CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y
|
||||
|
@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
idf_component_register(SRCS "test_common.c"
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES unity)
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES unity)
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components")
|
||||
|
||||
|
@ -5,6 +5,8 @@
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/queue.h>
|
||||
|
||||
#include "unity.h"
|
||||
#include "unity_test_runner.h"
|
||||
|
||||
@ -43,26 +45,57 @@ enum {
|
||||
TEST_REG_VAL4 = 0x4444
|
||||
};
|
||||
|
||||
typedef struct task_entry_s {
|
||||
TaskHandle_t task_handle;
|
||||
SemaphoreHandle_t task_sema_handle;
|
||||
void *inst_handle;
|
||||
LIST_ENTRY(task_entry_s) entries;
|
||||
} task_entry_t;
|
||||
|
||||
/**
|
||||
* @brief Start, stop helpers for common test module
|
||||
*
|
||||
*/
|
||||
void test_common_start();
|
||||
void test_common_stop();
|
||||
|
||||
/**
|
||||
* @brief Helper test functions for multi instance modbus master - slave test
|
||||
*
|
||||
*/
|
||||
TaskHandle_t test_slave_serial_create(mb_communication_info_t *pconfig);
|
||||
TaskHandle_t test_master_serial_create(mb_communication_info_t *pconfig, const mb_parameter_descriptor_t *pdescr, uint16_t descr_size);
|
||||
TaskHandle_t test_slave_tcp_create(mb_communication_info_t *pconfig);
|
||||
TaskHandle_t test_master_tcp_create(mb_communication_info_t *pconfig, const mb_parameter_descriptor_t *pdescr, uint16_t descr_size);
|
||||
TaskHandle_t test_slave_start_busy_task();
|
||||
// slave setup register area helper
|
||||
void test_slave_setup_start(void *mbs_handle);
|
||||
// Helper function to read characteristic from slave
|
||||
esp_err_t read_modbus_parameter(void *handle, uint16_t cid, uint16_t *par_data);
|
||||
// Helper function to write characteristic into slave
|
||||
esp_err_t write_modbus_parameter(void *handle, uint16_t cid, uint16_t *par_data);
|
||||
// TaskHandle_t test_common_slave_serial_create(mb_communication_info_t *pconfig);
|
||||
|
||||
// Common test check leak function
|
||||
TaskHandle_t test_common_slave_serial_create(mb_communication_info_t *pconfig, uint32_t priority);
|
||||
TaskHandle_t test_common_master_serial_create(mb_communication_info_t *pconfig,uint32_t priority, const mb_parameter_descriptor_t *pdescr, uint16_t descr_size);
|
||||
TaskHandle_t test_common_slave_tcp_create(mb_communication_info_t *pconfig, uint32_t priority);
|
||||
TaskHandle_t test_common_master_tcp_create(mb_communication_info_t *pconfig, uint32_t priority, const mb_parameter_descriptor_t *pdescr, uint16_t descr_size);
|
||||
TaskHandle_t test_common_start_busy_task(uint32_t priority);
|
||||
|
||||
/**
|
||||
* @brief The test helper function to check memory leak
|
||||
*
|
||||
*/
|
||||
void test_common_check_leak(size_t before_free, size_t after_free, const char *type, size_t warn_threshold, size_t critical_threshold);
|
||||
void test_slave_stop_busy_task(TaskHandle_t busy_task_handle);
|
||||
uint32_t test_common_wait_done(TickType_t timeout_ticks);
|
||||
void test_common_start();
|
||||
void test_common_stop();
|
||||
|
||||
// Slave setup register area helper
|
||||
void test_common_slave_setup_start(void *mbs_handle);
|
||||
// Helper function to read characteristic from slave
|
||||
esp_err_t test_common_read_modbus_parameter(void *handle, uint16_t cid, uint16_t *par_data);
|
||||
// Helper function to write characteristic into slave
|
||||
esp_err_t test_common_write_modbus_parameter(void *handle, uint16_t cid, uint16_t *par_data);
|
||||
|
||||
/**
|
||||
* @brief The test helper functions to work with test tasks
|
||||
*
|
||||
*/
|
||||
void test_common_task_notify_start(TaskHandle_t task_handle, uint32_t value);
|
||||
void test_common_task_start(TaskHandle_t task_handle, uint32_t value);
|
||||
int test_common_task_start_all(uint32_t value);
|
||||
bool test_common_task_wait_done(TaskHandle_t task_handle, TickType_t timeout_ticks);
|
||||
bool test_common_task_wait_done_delete(TaskHandle_t task_handle, TickType_t task_timeout_ticks);
|
||||
int test_common_task_wait_done_delete_all(TickType_t task_timeout_tick);
|
||||
void test_common_task_delete(TaskHandle_t task_handle);
|
||||
void test_common_task_delete_all();
|
||||
void *test_common_task_get_instance(TaskHandle_t task_handle);
|
||||
|
||||
|
||||
|
@ -43,15 +43,14 @@ if(CONFIG_MB_PORT_ADAPTER_EN)
|
||||
mbm_port_tcp_enable
|
||||
mbm_port_tcp_disable
|
||||
mbm_port_tcp_delete
|
||||
# mbm_port_timer_expired
|
||||
mbs_port_tcp_create
|
||||
mbs_port_tcp_enable
|
||||
mbs_port_tcp_disable
|
||||
mbs_port_tcp_send_data
|
||||
mbs_port_tcp_recv_data
|
||||
mbs_port_tcp_delete
|
||||
mb_port_evt_post
|
||||
mb_port_evt_get
|
||||
mb_port_event_post
|
||||
mb_port_event_get
|
||||
mb_port_ser_create
|
||||
mb_port_ser_recv_data
|
||||
mb_port_ser_send_data
|
||||
@ -69,4 +68,8 @@ endforeach()
|
||||
endif()
|
||||
|
||||
# allow multiple symbol definitions
|
||||
target_link_libraries(mb_ut_lib PUBLIC "-Wl,--allow-multiple-definition")
|
||||
target_link_libraries(mb_ut_lib PUBLIC "-Wl,--allow-multiple-definition")
|
||||
|
||||
if(CONFIG_MB_PORT_ADAPTER_EN)
|
||||
set_property(TARGET ${COMPONENT_LIB} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-u mb_test_include_stub_impl")
|
||||
endif()
|
@ -46,7 +46,7 @@ typedef struct _mb_adapter_port_entry
|
||||
uint16_t recv_length;
|
||||
uint64_t send_time_stamp;
|
||||
uint64_t recv_time_stamp;
|
||||
uint64_t test_timeout_us;
|
||||
_Atomic uint64_t test_timeout_us;
|
||||
uint32_t flags;
|
||||
mb_uid_info_t addr_info;
|
||||
QueueHandle_t rx_queue;
|
||||
@ -69,37 +69,37 @@ static QueueSetHandle_t queue_set = NULL;
|
||||
static TaskHandle_t adapter_task_handle; /*!< receive task handle */
|
||||
|
||||
IRAM_ATTR
|
||||
static bool mb_port_adapter_tmr_expired(void *inst)
|
||||
static bool mb_port_adapter_timer_expired(void *inst)
|
||||
{
|
||||
mb_port_adapter_t *port_obj = __containerof(inst, mb_port_adapter_t, base);
|
||||
|
||||
bool need_poll = false;
|
||||
mb_tmr_mode_enum_t timer_mode = mb_port_get_cur_tmr_mode(&port_obj->base);
|
||||
mb_timer_mode_enum_t timer_mode = mb_port_get_cur_timer_mode(&port_obj->base);
|
||||
|
||||
mb_port_tmr_disable(&port_obj->base);
|
||||
mb_port_timer_disable(&port_obj->base);
|
||||
|
||||
switch (timer_mode)
|
||||
{
|
||||
case MB_TMODE_T35:
|
||||
need_poll = mb_port_evt_post(&port_obj->base, EVENT(EV_READY));
|
||||
need_poll = mb_port_event_post(&port_obj->base, EVENT(EV_READY));
|
||||
ESP_EARLY_LOGD(TAG, "%p:EV_READY", port_obj->base.descr.parent);
|
||||
break;
|
||||
|
||||
case MB_TMODE_RESPOND_TIMEOUT:
|
||||
mb_port_evt_set_err_type(&port_obj->base, EV_ERROR_RESPOND_TIMEOUT);
|
||||
need_poll = mb_port_evt_post(&port_obj->base, EVENT(EV_ERROR_PROCESS));
|
||||
mb_port_event_set_err_type(&port_obj->base, EV_ERROR_RESPOND_TIMEOUT);
|
||||
need_poll = mb_port_event_post(&port_obj->base, EVENT(EV_ERROR_PROCESS));
|
||||
|
||||
ESP_EARLY_LOGW(TAG, "%p:EV_ERROR_RESPOND_TIMEOUT", port_obj->base.descr.parent);
|
||||
break;
|
||||
|
||||
case MB_TMODE_CONVERT_DELAY:
|
||||
/* If timer mode is convert delay, the master event then turns EV_MASTER_EXECUTE status. */
|
||||
need_poll = mb_port_evt_post(&port_obj->base, EVENT(EV_EXECUTE));
|
||||
need_poll = mb_port_event_post(&port_obj->base, EVENT(EV_EXECUTE));
|
||||
ESP_EARLY_LOGD(TAG, "%p:MB_TMODE_CONVERT_DELAY", port_obj->base.descr.parent);
|
||||
break;
|
||||
|
||||
default:
|
||||
need_poll = mb_port_evt_post(&port_obj->base, EVENT(EV_READY));
|
||||
need_poll = mb_port_event_post(&port_obj->base, EVENT(EV_READY));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -200,7 +200,7 @@ static void mb_port_adapter_timer_cb(void *param)
|
||||
// Send the data to all ports with the same communication port setting except itself
|
||||
queue_push(it->rx_queue, (void *)&temp_buffer[0], sz, NULL);
|
||||
mb_port_adapter_set_flag(&port_obj->base, MB_QUEUE_FLAG_SENT);
|
||||
ESP_LOGW(TAG, "Send (%d bytes) from %s to %s. ", (int)sz, port_obj->base.descr.parent_name, it->base.descr.parent_name);
|
||||
ESP_LOGD(TAG, "Send (%d bytes) from %s to %s. ", (int)sz, port_obj->base.descr.parent_name, it->base.descr.parent_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -228,7 +228,7 @@ static void mb_port_adapter_conn_logic(void *inst, mb_uid_info_t *paddr_info)
|
||||
&& !slave->base.descr.is_master
|
||||
&& (paddr_info->port == slave->addr_info.port)) {
|
||||
// Register each slave object
|
||||
ESP_LOGW(TAG, "Check connection state of object #%d(%s), uid: %d, port: %d, %s",
|
||||
ESP_LOGD(TAG, "Check connection state of object #%d(%s), uid: %d, port: %d, %s",
|
||||
paddr_info->index, paddr_info->node_name_str,
|
||||
paddr_info->uid, paddr_info->port,
|
||||
(paddr_info->state == MB_SOCK_STATE_CONNECTED) ? "CONNECTED" : "DISCONNECTED");
|
||||
@ -249,7 +249,7 @@ static void mb_port_adapter_conn_logic(void *inst, mb_uid_info_t *paddr_info)
|
||||
vTaskDelay(MB_ADAPTER_CONN_TIMEOUT);
|
||||
}
|
||||
} else { // slave connection logic
|
||||
ESP_LOGW(TAG, "Register connection in adapter object #%d(%s), uid: %d, port: %d, to master %s",
|
||||
ESP_LOGD(TAG, "Register connection in adapter object #%d(%s), uid: %d, port: %d, to master %s",
|
||||
port_obj->addr_info.index, port_obj->addr_info.node_name_str,
|
||||
port_obj->addr_info.uid, port_obj->addr_info.port, paddr_info->node_name_str);
|
||||
// Mimic connection logic for each slave here
|
||||
@ -279,8 +279,8 @@ static void mb_port_adapter_task(void *p_args)
|
||||
if (it && active_queue && (it->rx_queue == active_queue)) {
|
||||
if (xQueuePeek(it->rx_queue, &frame_entry, 0) == pdTRUE) {
|
||||
it->recv_length = frame_entry.len;
|
||||
mb_port_evt_post(&it->base, EVENT(EV_FRAME_RECEIVED, frame_entry.len, NULL, 0));
|
||||
ESP_LOGW(TAG, "%s, frame %d bytes is ready.", (it->base.descr.parent_name), (int)frame_entry.len);
|
||||
mb_port_event_post(&it->base, EVENT(EV_FRAME_RECEIVED, frame_entry.len, NULL, 0));
|
||||
ESP_LOGD(TAG, "%s, frame %d bytes is ready.", (it->base.descr.parent_name), (int)frame_entry.len);
|
||||
}
|
||||
} else if (it && (it->conn_queue == active_queue)) {
|
||||
if (xQueueReceive(it->conn_queue, &addr_info, MB_ADAPTER_QUEUE_TIMEOUT) == pdTRUE) {
|
||||
@ -312,7 +312,7 @@ static mb_err_enum_t mb_port_adapter_connect(mb_tcp_opts_t *tcp_opts, void *pobj
|
||||
int res = port_scan_addr_string((char *)*paddr_table, &uid_info);
|
||||
if (res > 0)
|
||||
{
|
||||
ESP_LOGW(TAG, "Config: %s, IP: %s, port: %d, slave_addr: %d, ip_ver: %s",
|
||||
ESP_LOGD(TAG, "Config: %s, IP: %s, port: %d, slave_addr: %d, ip_ver: %s",
|
||||
(char *)*paddr_table, uid_info.ip_addr_str, uid_info.port,
|
||||
uid_info.uid, (uid_info.addr_type == MB_IPV4 ? "IPV4" : "IPV6"));
|
||||
uid_info.index = count++;
|
||||
@ -337,7 +337,7 @@ static mb_err_enum_t mb_port_adapter_connect(mb_tcp_opts_t *tcp_opts, void *pobj
|
||||
}
|
||||
paddr_table++;
|
||||
}
|
||||
ESP_LOGW(TAG, "parsed and added %d slave configurations.", count);
|
||||
ESP_LOGD(TAG, "parsed and added %d slave configurations.", count);
|
||||
return count ? MB_ENOERR : MB_EINVAL;
|
||||
}
|
||||
|
||||
@ -357,7 +357,8 @@ mb_err_enum_t mb_port_adapter_create(mb_uid_info_t *paddr_info, mb_port_base_t *
|
||||
.callback = mb_port_adapter_timer_cb,
|
||||
.arg = padapter,
|
||||
.dispatch_method = ESP_TIMER_TASK,
|
||||
.name = padapter->base.descr.parent_name};
|
||||
.name = padapter->base.descr.parent_name
|
||||
};
|
||||
// Create Modbus timer handlers for streams
|
||||
MB_GOTO_ON_ERROR(esp_timer_create(&timer_conf, &padapter->timer_handle),
|
||||
error, TAG, "create input stream timer failed.");
|
||||
@ -404,7 +405,7 @@ mb_err_enum_t mb_port_adapter_create(mb_uid_info_t *paddr_info, mb_port_base_t *
|
||||
padapter->base.descr.parent_name, (unsigned)paddr_info->port);
|
||||
MB_GOTO_ON_FALSE((res), MB_EILLSTATE, error,
|
||||
TAG, "object adress info alloc fail, err: %d", (int)res);
|
||||
padapter->base.cb.tmr_expired = mb_port_adapter_tmr_expired;
|
||||
padapter->base.cb.tmr_expired = mb_port_adapter_timer_expired;
|
||||
padapter->base.cb.tx_empty = NULL;
|
||||
padapter->base.cb.byte_rcvd = NULL;
|
||||
padapter->base.arg = (void *)padapter;
|
||||
@ -414,7 +415,7 @@ mb_err_enum_t mb_port_adapter_create(mb_uid_info_t *paddr_info, mb_port_base_t *
|
||||
padapter->addr_info.node_name_str = pstr;
|
||||
padapter->addr_info.ip_addr_str = pstr;
|
||||
*in_out_obj = &(padapter->base);
|
||||
ESP_LOGW(TAG, "created object @%p, from parent %p", padapter, padapter->base.descr.parent);
|
||||
ESP_LOGD(TAG, "created object @%p, from parent %p", padapter, padapter->base.descr.parent);
|
||||
return MB_ENOERR;
|
||||
|
||||
error:
|
||||
@ -447,7 +448,7 @@ mb_err_enum_t mb_port_adapter_tcp_create(mb_tcp_opts_t *tcp_opts, mb_port_base_t
|
||||
MB_GOTO_ON_FALSE((ret == MB_ENOERR), MB_EILLSTATE, error, TAG,
|
||||
"%s, could not parse config, err=%x.", pobj->descr.parent_name, (int)ret);
|
||||
}
|
||||
ESP_LOGW(TAG, "%s, set test time to %" PRIu64, pobj->descr.parent_name, tcp_opts->test_tout_us);
|
||||
ESP_LOGD(TAG, "%s, set test time to %" PRIu64, pobj->descr.parent_name, tcp_opts->test_tout_us);
|
||||
mb_port_adapter_set_response_time(pobj, (tcp_opts->test_tout_us));
|
||||
}
|
||||
*in_out_obj = pobj;
|
||||
@ -478,7 +479,7 @@ mb_err_enum_t mb_port_adapter_ser_create(mb_serial_opts_t *ser_opts, mb_port_bas
|
||||
mb_port_base_t *pobj = *in_out_obj;
|
||||
mb_err_enum_t ret = mb_port_adapter_create(&addr_info, &pobj);
|
||||
if ((ret == MB_ENOERR) && pobj) {
|
||||
ESP_LOGW(TAG, "%s, set test time to %d", pobj->descr.parent_name, (int)(ser_opts->test_tout_us));
|
||||
ESP_LOGD(TAG, "%s, set test time to %d", pobj->descr.parent_name, (int)(ser_opts->test_tout_us));
|
||||
mb_port_adapter_set_response_time(pobj, (ser_opts->test_tout_us));
|
||||
*in_out_obj = pobj;
|
||||
}
|
||||
@ -576,14 +577,14 @@ bool mb_port_adapter_recv_data(mb_port_base_t *inst, uint8_t **ppframe, uint16_t
|
||||
int length = queue_pop(port_obj->rx_queue, &port_obj->rx_buffer[0], CONFIG_FMB_BUFFER_SIZE, NULL);
|
||||
if (length)
|
||||
{
|
||||
mb_port_tmr_disable(inst);
|
||||
ESP_LOGW(TAG, "%s, received data: %d bytes.", inst->descr.parent_name, length);
|
||||
mb_port_timer_disable(inst);
|
||||
ESP_LOGD(TAG, "%s, received data: %d bytes.", inst->descr.parent_name, length);
|
||||
// Stop timer because the new data is received
|
||||
// Store the timestamp of received frame
|
||||
port_obj->recv_time_stamp = esp_timer_get_time();
|
||||
*ppframe = &port_obj->rx_buffer[0];
|
||||
ESP_LOG_BUFFER_HEX_LEVEL(MB_STR_CAT(inst->descr.parent_name, ":PORT_RECV"),
|
||||
(void *)&port_obj->rx_buffer[0], (uint16_t)length, ESP_LOG_WARN);
|
||||
(void *)&port_obj->rx_buffer[0], (uint16_t)length, ESP_LOG_DEBUG);
|
||||
}
|
||||
CRITICAL_SECTION_UNLOCK(inst->lock);
|
||||
}
|
||||
@ -615,9 +616,9 @@ bool mb_port_adapter_send_data(mb_port_base_t *inst, uint8_t address, uint8_t *p
|
||||
port_obj->send_time_stamp = esp_timer_get_time();
|
||||
// Print sent packet, the tag used is more clear to see
|
||||
ESP_LOG_BUFFER_HEX_LEVEL(MB_STR_CAT(inst->descr.parent_name, ":PORT_SEND"),
|
||||
(void *)pframe, length, ESP_LOG_WARN);
|
||||
(void)mb_port_evt_post(inst, EVENT(EV_FRAME_SENT));
|
||||
ESP_LOGW(TAG, "%s, tx completed, flags = 0x%04x.", inst->descr.parent_name, (int)flags);
|
||||
(void *)pframe, length, ESP_LOG_DEBUG);
|
||||
(void)mb_port_event_post(inst, EVENT(EV_FRAME_SENT));
|
||||
ESP_LOGD(TAG, "%s, tx completed, flags = 0x%04x.", inst->descr.parent_name, (int)flags);
|
||||
res = true;
|
||||
|
||||
}
|
||||
|
@ -3,12 +3,10 @@
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdatomic.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
|
||||
#include "esp_timer.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
@ -24,11 +22,18 @@
|
||||
#include "port_adapter.h"
|
||||
#include "port_stubs.h"
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static const char *TAG = "port_stub";
|
||||
static __attribute__((unused)) const char *TAG = "port_stub";
|
||||
|
||||
#if (CONFIG_MB_PORT_ADAPTER_EN)
|
||||
|
||||
// The workaround to statically link whole test library
|
||||
__attribute__((unused)) bool mb_test_include_stub_impl = true;
|
||||
|
||||
// Below are function wrappers to substitute actual port object with the adapter object for test purpose
|
||||
|
||||
@ -66,18 +71,18 @@ void __wrap_mb_port_ser_disable(mb_port_base_t *inst)
|
||||
|
||||
#endif
|
||||
|
||||
IRAM_ATTR
|
||||
bool __wrap_mb_port_evt_get(mb_port_base_t *inst, mb_event_t *pevent)
|
||||
IRAM_ATTR
|
||||
bool __wrap_mb_port_event_get(mb_port_base_t *inst, mb_event_t *pevent)
|
||||
{
|
||||
bool result = __real_mb_port_evt_get(inst, pevent);
|
||||
ESP_LOGW(TAG, "%s, get event:%x.", inst->descr.parent_name, pevent->event);
|
||||
bool result = __real_mb_port_event_get(inst, pevent);
|
||||
ESP_LOGD(TAG, "%s, get event:%x.", inst->descr.parent_name, pevent->event);
|
||||
return result;
|
||||
}
|
||||
|
||||
IRAM_ATTR
|
||||
bool __wrap_mb_port_evt_post(mb_port_base_t *inst, mb_event_t event)
|
||||
IRAM_ATTR
|
||||
bool __wrap_mb_port_event_post(mb_port_base_t *inst, mb_event_t event)
|
||||
{
|
||||
bool result = __real_mb_port_evt_post(inst, event);
|
||||
bool result = __real_mb_port_event_post(inst, event);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -86,7 +91,7 @@ bool __wrap_mb_port_evt_post(mb_port_base_t *inst, mb_event_t event)
|
||||
// Below are the TCP port function wrappers to exchange the port layer to TCP adapter
|
||||
mb_err_enum_t __wrap_mbm_port_tcp_create(mb_tcp_opts_t *tcp_opts, mb_port_base_t **in_out_obj)
|
||||
{
|
||||
ESP_LOGW(TAG, "master tcp adapter installed.");
|
||||
ESP_LOGD(TAG, "master tcp adapter installed.");
|
||||
return mb_port_adapter_tcp_create(tcp_opts, in_out_obj);
|
||||
}
|
||||
|
||||
@ -107,37 +112,31 @@ void __wrap_mbm_port_tcp_delete(mb_port_base_t *inst)
|
||||
|
||||
void __wrap_mbm_port_tcp_enable(mb_port_base_t *inst)
|
||||
{
|
||||
ESP_LOGW(TAG, "adapter master tcp enable port.");
|
||||
ESP_LOGD(TAG, "adapter master tcp enable port.");
|
||||
}
|
||||
|
||||
void __wrap_mbm_port_tcp_disable(mb_port_base_t *inst)
|
||||
{
|
||||
ESP_LOGW(TAG, "adapter master tcp disable port.");
|
||||
ESP_LOGD(TAG, "adapter master tcp disable port.");
|
||||
}
|
||||
|
||||
void __wrap_mbm_port_tcp_set_conn_cb(mb_port_base_t *inst, void *conn_fp, void *arg)
|
||||
{
|
||||
ESP_LOGW(TAG, "adapter set connection callback.");
|
||||
ESP_LOGD(TAG, "adapter set connection callback.");
|
||||
mb_port_adapter_tcp_set_conn_cb(inst, conn_fp, arg);
|
||||
}
|
||||
|
||||
mb_uid_info_t *__wrap_mbm_port_tcp_get_slave_info(mb_port_base_t *inst, uint8_t slave_addr, mb_sock_state_t exp_state)
|
||||
{
|
||||
ESP_LOGW(TAG, "adapter get slave #%d info.", slave_addr);
|
||||
ESP_LOGD(TAG, "adapter get slave #%d info.", slave_addr);
|
||||
return mb_port_adapter_get_slave_info(inst, slave_addr, exp_state);
|
||||
}
|
||||
|
||||
// IRAM_ATTR
|
||||
// bool __wrap_mbm_port_timer_expired(void *inst)
|
||||
// {
|
||||
// return mb_port_adapter_tmr_expired(inst);
|
||||
// }
|
||||
|
||||
// Wrappers for modbus slave tcp
|
||||
|
||||
mb_err_enum_t __wrap_mbs_port_tcp_create(mb_tcp_opts_t *tcp_opts, mb_port_base_t **in_out_obj)
|
||||
{
|
||||
ESP_LOGW(TAG, "install slave tcp adapter.");
|
||||
ESP_LOGD(TAG, "install slave tcp adapter.");
|
||||
return mb_port_adapter_tcp_create(tcp_opts, in_out_obj);
|
||||
}
|
||||
|
||||
@ -158,16 +157,18 @@ void __wrap_mbs_port_tcp_delete(mb_port_base_t *inst)
|
||||
|
||||
void __wrap_mbs_port_tcp_enable(mb_port_base_t *inst)
|
||||
{
|
||||
ESP_LOGW(TAG, "adapter slave tcp enable port.");
|
||||
ESP_LOGD(TAG, "adapter slave tcp enable port.");
|
||||
}
|
||||
|
||||
void __wrap_mbs_port_tcp_disable(mb_port_base_t *inst)
|
||||
{
|
||||
ESP_LOGW(TAG, "adapter slave tcp disable port.");
|
||||
ESP_LOGD(TAG, "adapter slave tcp disable port.");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif // CONFIG_MB_PORT_ADAPTER_EN
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -6,17 +6,17 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sdkconfig.h>
|
||||
#include "esp_log.h"
|
||||
#include "mb_common.h"
|
||||
#include "mb_port_types.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
// Serial port function wrappers
|
||||
|
||||
bool __wrap_mb_port_evt_get(mb_port_base_t *inst, mb_event_t *pevent);
|
||||
bool __wrap_mb_port_evt_post(mb_port_base_t *inst, mb_event_t event);
|
||||
extern bool __real_mb_port_evt_get(mb_port_base_t *inst, mb_event_t *pevent);
|
||||
extern bool __real_mb_port_evt_post(mb_port_base_t *inst, mb_event_t event);
|
||||
bool __wrap_mb_port_event_get(mb_port_base_t *inst, mb_event_t *pevent);
|
||||
bool __wrap_mb_port_event_post(mb_port_base_t *inst, mb_event_t event);
|
||||
extern bool __real_mb_port_event_get(mb_port_base_t *inst, mb_event_t *pevent);
|
||||
extern bool __real_mb_port_event_post(mb_port_base_t *inst, mb_event_t event);
|
||||
|
||||
#if (CONFIG_FMB_COMM_MODE_ASCII_EN || CONFIG_FMB_COMM_MODE_RTU_EN)
|
||||
|
||||
@ -46,7 +46,7 @@ void __wrap_mbm_port_tcp_enable(mb_port_base_t *inst);
|
||||
void __wrap_mbm_port_tcp_disable(mb_port_base_t *inst);
|
||||
void __wrap_mbm_port_tcp_set_conn_cb(mb_port_base_t *inst, void *conn_fp, void *arg);
|
||||
mb_uid_info_t *__wrap_mbm_port_tcp_get_slave_info(mb_port_base_t *inst, uint8_t uid, mb_sock_state_t exp_state);
|
||||
//bool __wrap_mbm_port_timer_expired(void *inst);
|
||||
//bool __wrap_mbm_port__expired(void *inst);
|
||||
|
||||
extern void __real_mbm_port_tcp_enable(mb_port_base_t *inst);
|
||||
extern void __real_mbm_port_tcp_disable(mb_port_base_t *inst);
|
||||
|
@ -21,28 +21,27 @@
|
||||
#include "esp_heap_trace.h"
|
||||
#endif
|
||||
|
||||
#define TEST_TASK_PRIO_MASTER CONFIG_MB_TEST_MASTER_TASK_PRIO
|
||||
#define TEST_TASK_PRIO_SLAVE CONFIG_MB_TEST_SLAVE_TASK_PRIO
|
||||
#define TEST_TASK_STACK_SIZE 5120
|
||||
#define TEST_TASK_CYCLE_COUNTER CONFIG_MB_TEST_COMM_CYCLE_COUNTER
|
||||
#define TEST_BUSY_TASK_PRIO 20
|
||||
#define TEST_TASK_PRIO_MASTER (CONFIG_MB_TEST_MASTER_TASK_PRIO)
|
||||
#define TEST_TASK_PRIO_SLAVE (CONFIG_MB_TEST_SLAVE_TASK_PRIO)
|
||||
#define TEST_TASK_STACK_SIZE (5120)
|
||||
#define TEST_TASK_CYCLE_COUNTER (CONFIG_MB_TEST_COMM_CYCLE_COUNTER)
|
||||
#define TEST_BUSY_TASK_PRIO (20)
|
||||
|
||||
#define TEST_REG_START_AREA0 (0x0000)
|
||||
#define TEST_READ_MASK (MB_EVENT_HOLDING_REG_RD | MB_EVENT_INPUT_REG_RD | MB_EVENT_DISCRETE_RD | MB_EVENT_COILS_RD)
|
||||
#define TEST_WRITE_MASK (MB_EVENT_HOLDING_REG_WR | MB_EVENT_COILS_WR)
|
||||
#define TEST_READ_WRITE_MASK (TEST_WRITE_MASK | TEST_READ_MASK)
|
||||
|
||||
#define TEST_BUSY_COUNT 150000
|
||||
|
||||
#define TEST_PAR_INFO_GET_TOUT (10)
|
||||
#define TEST_SEND_TIMEOUT (200 / portTICK_PERIOD_MS)
|
||||
#define TEST_TASK_START_TIMEOUT (5000 / portTICK_PERIOD_MS)
|
||||
|
||||
#define TEST_NOTIF_SEND_TOUT 100
|
||||
#define TEST_NOTIF_SIZE 20
|
||||
#define TEST_ALLOW_PROC_FAIL 2 // percentage of allowed failures
|
||||
|
||||
#define TEST_TASK_TICK_TIME (20 / portTICK_PERIOD_MS)
|
||||
#define TEST_REG_START_AREA0 (0x0000)
|
||||
#define TEST_READ_MASK (MB_EVENT_HOLDING_REG_RD |\
|
||||
MB_EVENT_INPUT_REG_RD |\
|
||||
MB_EVENT_DISCRETE_RD |\
|
||||
MB_EVENT_COILS_RD)
|
||||
#define TEST_WRITE_MASK (MB_EVENT_HOLDING_REG_WR | MB_EVENT_COILS_WR)
|
||||
#define TEST_READ_WRITE_MASK (TEST_WRITE_MASK | TEST_READ_MASK)
|
||||
#define TEST_BUSY_COUNT (150000)
|
||||
#define TEST_PAR_INFO_GET_TOUT (10)
|
||||
#define TEST_SEND_TIMEOUT (200 / portTICK_PERIOD_MS)
|
||||
#define TEST_TASK_START_TIMEOUT (10000 / portTICK_PERIOD_MS)
|
||||
#define TEST_NOTIF_SEND_TOUT (400 / portTICK_PERIOD_MS)
|
||||
#define TEST_NOTIF_SIZE (20)
|
||||
#define TEST_ALLOW_PROC_FAIL (5) // percentage of allowed failures due to desynchronization
|
||||
#define TEST_TASK_TICK_TIME (50 / portTICK_PERIOD_MS)
|
||||
|
||||
#define TAG "TEST_COMMON"
|
||||
|
||||
@ -59,15 +58,15 @@ const uint16_t holding_registers_counter = (sizeof(holding_registers) / sizeof(h
|
||||
const uint16_t input_registers_counter = (sizeof(input_registers) / sizeof(input_registers[0]));
|
||||
const uint16_t coil_registers_counter = (sizeof(coil_registers) / sizeof(coil_registers[0]));
|
||||
|
||||
static QueueHandle_t tasks_done_queue = NULL;
|
||||
static int test_error_counter = 0;
|
||||
static int test_good_counter = 0;
|
||||
|
||||
static size_t before_free_8bit = 0;
|
||||
static size_t before_free_32bit = 0;
|
||||
|
||||
// Heap memory leak traicing
|
||||
#ifdef CONFIG_HEAP_TRACING
|
||||
#define NUM_RECORDS 200
|
||||
#define NUM_RECORDS 500
|
||||
static heap_trace_record_t trace_record[NUM_RECORDS];
|
||||
#endif
|
||||
|
||||
@ -87,6 +86,82 @@ static heap_trace_record_t trace_record[NUM_RECORDS];
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
// The linked list of test tasks instances
|
||||
LIST_HEAD(task_entry, task_entry_s) s_task_list;
|
||||
|
||||
static portMUX_TYPE s_list_spinlock = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
static void task_entry_remove(task_entry_t *task_entry)
|
||||
{
|
||||
portENTER_CRITICAL(&s_list_spinlock);
|
||||
LIST_REMOVE(task_entry, entries);
|
||||
portEXIT_CRITICAL(&s_list_spinlock);
|
||||
ESP_LOGD(TAG, "Delete task 0x%" PRIx32, (uint32_t)task_entry->task_handle);
|
||||
vTaskDelete(task_entry->task_handle);
|
||||
vSemaphoreDelete(task_entry->task_sema_handle);
|
||||
free(task_entry);
|
||||
}
|
||||
|
||||
static bool task_wait_done_and_remove(task_entry_t *task_entry, TickType_t tout_ticks)
|
||||
{
|
||||
bool is_done = false;
|
||||
if (task_entry && task_entry->task_handle && task_entry->task_sema_handle) {
|
||||
if ((xSemaphoreTake(task_entry->task_sema_handle, tout_ticks) == pdTRUE)) {
|
||||
ESP_LOGI(TAG, "Test task 0x%" PRIx32 ", done successfully.", (uint32_t)task_entry->task_handle);
|
||||
is_done = true;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Could not complete task 0x%" PRIx32 " after timeout, force kill the task.",
|
||||
(uint32_t)task_entry->task_handle);
|
||||
is_done = false;
|
||||
}
|
||||
vTaskDelay(1); // Let the lower priority task to suspend or delete itself
|
||||
task_entry_remove(task_entry);
|
||||
}
|
||||
return (is_done);
|
||||
}
|
||||
|
||||
static void test_task_add_entry(TaskHandle_t task_handle, void *pinst)
|
||||
{
|
||||
TEST_ASSERT_TRUE(task_handle);
|
||||
task_entry_t *new_entry = (task_entry_t*) calloc(1, sizeof(task_entry_t));
|
||||
TEST_ASSERT_TRUE(new_entry);
|
||||
portENTER_CRITICAL(&s_list_spinlock);
|
||||
new_entry->task_handle = task_handle;
|
||||
new_entry->task_sema_handle = xSemaphoreCreateBinary();
|
||||
new_entry->inst_handle = pinst;
|
||||
LIST_INSERT_HEAD(&s_task_list, new_entry, entries);
|
||||
portEXIT_CRITICAL(&s_list_spinlock);
|
||||
xSemaphoreTake(new_entry->task_sema_handle, 1);
|
||||
}
|
||||
|
||||
static task_entry_t *test_task_find_entry(TaskHandle_t task_handle)
|
||||
{
|
||||
TEST_ASSERT_NOT_NULL(task_handle);
|
||||
|
||||
task_entry_t *it, *pfound = NULL;
|
||||
if (LIST_EMPTY(&s_task_list)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
portENTER_CRITICAL(&s_list_spinlock);
|
||||
LIST_FOREACH(it, &s_task_list, entries) {
|
||||
if (it->task_handle == task_handle) {
|
||||
pfound = it;
|
||||
break;
|
||||
}
|
||||
}
|
||||
portEXIT_CRITICAL(&s_list_spinlock);
|
||||
return pfound;
|
||||
}
|
||||
|
||||
static void test_common_task_notify_done(TaskHandle_t task_handle)
|
||||
{
|
||||
task_entry_t *it = test_task_find_entry(task_handle);
|
||||
if (it) {
|
||||
xSemaphoreGive(it->task_sema_handle);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_busy_task(void *phandle)
|
||||
{
|
||||
spinlock_t spin_lock;
|
||||
@ -102,21 +177,95 @@ static void test_busy_task(void *phandle)
|
||||
}
|
||||
}
|
||||
|
||||
// Start the high priority task to mimic the case when the modbus
|
||||
// tasks do not get time quota from RTOS.
|
||||
TaskHandle_t test_slave_start_busy_task()
|
||||
void test_common_task_start(TaskHandle_t task_handle, uint32_t value)
|
||||
{
|
||||
TaskHandle_t busy_task_handle = NULL;
|
||||
TEST_ASSERT_TRUE(xTaskCreatePinnedToCore(test_busy_task, "busy_task",
|
||||
TEST_TASK_STACK_SIZE,
|
||||
NULL, TEST_BUSY_TASK_PRIO,
|
||||
&busy_task_handle, MB_PORT_TASK_AFFINITY));
|
||||
return busy_task_handle;
|
||||
// Directly notify the task waiting to start loop
|
||||
test_common_task_notify_start(task_handle, value);
|
||||
}
|
||||
|
||||
void test_slave_stop_busy_task(TaskHandle_t busy_task_handle)
|
||||
int test_common_task_start_all(uint32_t value)
|
||||
{
|
||||
vTaskDelete(busy_task_handle);
|
||||
task_entry_t *it = NULL;
|
||||
if (LIST_EMPTY(&s_task_list)) {
|
||||
return 0;
|
||||
}
|
||||
int task_count = 0;
|
||||
LIST_FOREACH(it, &s_task_list, entries) {
|
||||
test_common_task_notify_start(it->task_handle, value);
|
||||
task_count++;
|
||||
}
|
||||
return task_count;
|
||||
}
|
||||
|
||||
bool test_common_task_wait_done(TaskHandle_t task_handle, TickType_t timeout_ticks)
|
||||
{
|
||||
task_entry_t *it = test_task_find_entry(task_handle);
|
||||
if (it && (xSemaphoreTake(it->task_sema_handle, timeout_ticks) == pdTRUE)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool test_common_task_wait_done_delete(TaskHandle_t task_handle, TickType_t task_timeout_ticks)
|
||||
{
|
||||
task_entry_t *it = test_task_find_entry(task_handle);
|
||||
return task_wait_done_and_remove(it, task_timeout_ticks);
|
||||
}
|
||||
|
||||
int test_common_task_wait_done_delete_all(TickType_t task_timeout_tick)
|
||||
{
|
||||
task_entry_t *it, *ptmp = NULL;
|
||||
int task_count = 0;
|
||||
if (LIST_EMPTY(&s_task_list)) {
|
||||
return 0;
|
||||
}
|
||||
LIST_FOREACH_SAFE(it, &s_task_list, entries, ptmp) {
|
||||
task_wait_done_and_remove(it, task_timeout_tick);
|
||||
task_count++;
|
||||
}
|
||||
return task_count;
|
||||
}
|
||||
|
||||
void test_common_task_delete(TaskHandle_t task_handle)
|
||||
{
|
||||
task_entry_t *it = test_task_find_entry(task_handle);
|
||||
if (it) {
|
||||
task_entry_remove(it);
|
||||
}
|
||||
}
|
||||
|
||||
void test_common_task_delete_all()
|
||||
{
|
||||
task_entry_t *it = NULL;
|
||||
while ((it = LIST_FIRST(&s_task_list))) {
|
||||
task_entry_remove(it);
|
||||
}
|
||||
}
|
||||
|
||||
void *test_common_task_get_instance(TaskHandle_t task_handle)
|
||||
{
|
||||
task_entry_t *it = test_task_find_entry(task_handle);
|
||||
if (it) {
|
||||
return it->inst_handle;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Start the high priority task to mimic the case when the modbus
|
||||
// tasks do not get time quota from RTOS.
|
||||
TaskHandle_t test_common_start_busy_task(uint32_t priority)
|
||||
{
|
||||
TaskHandle_t busy_task_handle = NULL;
|
||||
if (!priority) {
|
||||
priority = TEST_BUSY_TASK_PRIO;
|
||||
}
|
||||
|
||||
TEST_ASSERT_TRUE(xTaskCreatePinnedToCore(test_busy_task, "busy_task",
|
||||
TEST_TASK_STACK_SIZE,
|
||||
NULL, priority,
|
||||
&busy_task_handle, MB_PORT_TASK_AFFINITY));
|
||||
test_task_add_entry(busy_task_handle, NULL);
|
||||
return busy_task_handle;
|
||||
}
|
||||
|
||||
void test_common_start()
|
||||
@ -131,47 +280,12 @@ void test_common_start()
|
||||
#ifdef CONFIG_HEAP_TRACING
|
||||
ESP_ERROR_CHECK( heap_trace_start(HEAP_TRACE_LEAKS) );
|
||||
#endif
|
||||
|
||||
tasks_done_queue = xQueueCreate(TEST_NOTIF_SIZE, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
uint32_t test_common_wait_done(TickType_t timeout_ticks)
|
||||
{
|
||||
uint32_t tmp_val = 0;
|
||||
|
||||
if (xQueueReceive(tasks_done_queue, &tmp_val, timeout_ticks) == pdTRUE) {
|
||||
return tmp_val;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t test_comon_notify_done(uint32_t value)
|
||||
{
|
||||
uint32_t tmp_val = value;
|
||||
return (uint32_t)xQueueSend(tasks_done_queue, (const void*)&tmp_val, TEST_NOTIF_SEND_TOUT);
|
||||
}
|
||||
|
||||
static uint32_t test_common_task_wait_start(TickType_t timeout_ticks)
|
||||
{
|
||||
static uint32_t notify_value = 0;
|
||||
|
||||
if (xTaskNotifyWait(0, 0, ¬ify_value, timeout_ticks) == pdTRUE) {
|
||||
ESP_LOGW(TAG, "Task: 0x%" PRIx32 ", get notify value = %d",
|
||||
(uint32_t)xTaskGetCurrentTaskHandle(), (int)notify_value);
|
||||
return pdTRUE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void test_common_task_notify_start(TaskHandle_t task_handle, uint32_t value)
|
||||
{
|
||||
ESP_LOGW(TAG, "Notify task 0x%" PRIx32, (uint32_t)task_handle);
|
||||
TEST_ASSERT_EQUAL_INT(xTaskNotify(task_handle, value, eSetValueWithOverwrite), pdTRUE);
|
||||
LIST_INIT(&s_task_list);
|
||||
}
|
||||
|
||||
void test_common_stop()
|
||||
{
|
||||
vQueueDelete(tasks_done_queue);
|
||||
//vQueueDelete(tasks_done_queue);
|
||||
holding_registers[CID_DEV_REG_COUNT] = 0;
|
||||
test_error_counter = 0;
|
||||
test_good_counter = 0;
|
||||
@ -183,7 +297,6 @@ void test_common_stop()
|
||||
ESP_ERROR_CHECK( heap_trace_stop() );
|
||||
heap_trace_dump();
|
||||
#endif
|
||||
|
||||
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||
test_common_check_leak(before_free_8bit, after_free_8bit, "8BIT",
|
||||
@ -192,11 +305,29 @@ void test_common_stop()
|
||||
CONFIG_MB_TEST_LEAK_WARN_LEVEL, CONFIG_MB_TEST_LEAK_CRITICAL_LEVEL);
|
||||
}
|
||||
|
||||
static uint32_t test_common_task_wait_start(TickType_t timeout_ticks)
|
||||
{
|
||||
static uint32_t notify_value = 0;
|
||||
|
||||
if (xTaskNotifyWait(0, 0, ¬ify_value, timeout_ticks) == pdTRUE) {
|
||||
ESP_LOGD(TAG, "Task: 0x%" PRIx32 ", get notify value = %u",
|
||||
(uint32_t)xTaskGetCurrentTaskHandle(), (unsigned)notify_value);
|
||||
return pdTRUE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void test_common_task_notify_start(TaskHandle_t task_handle, uint32_t value)
|
||||
{
|
||||
ESP_LOGD(TAG, "Notify task start 0x%" PRIx32, (uint32_t)task_handle);
|
||||
TEST_ASSERT_EQUAL_INT(xTaskNotify(task_handle, value, eSetValueWithOverwrite), pdTRUE);
|
||||
}
|
||||
|
||||
void test_common_check_leak(size_t before_free,
|
||||
size_t after_free,
|
||||
const char *type,
|
||||
size_t warn_threshold,
|
||||
size_t critical_threshold)
|
||||
size_t after_free,
|
||||
const char *type,
|
||||
size_t warn_threshold,
|
||||
size_t critical_threshold)
|
||||
{
|
||||
int free_delta = (int)after_free - (int)before_free;
|
||||
printf("MALLOC_CAP_%s usage: Free memory delta: %d Leak threshold: -%u \n",
|
||||
@ -222,7 +353,7 @@ void test_common_check_leak(size_t before_free,
|
||||
}
|
||||
|
||||
// Helper function to read one characteristic from slave
|
||||
esp_err_t read_modbus_parameter(void *handle, uint16_t cid, uint16_t *par_data)
|
||||
esp_err_t test_common_read_modbus_parameter(void *handle, uint16_t cid, uint16_t *par_data)
|
||||
{
|
||||
const mb_parameter_descriptor_t *param_descriptor = NULL;
|
||||
|
||||
@ -312,16 +443,16 @@ static void test_master_task(void *arg)
|
||||
switch (req_type)
|
||||
{
|
||||
case RT_HOLDING_RD:
|
||||
err = read_modbus_parameter(mbm_handle, CID_DEV_REG0, &holding_registers[CID_DEV_REG0]);
|
||||
err = test_common_read_modbus_parameter(mbm_handle, CID_DEV_REG0, &holding_registers[CID_DEV_REG0]);
|
||||
CHECK_PAR_VALUE(CID_DEV_REG0, err, holding_registers[CID_DEV_REG0], TEST_REG_VAL1);
|
||||
|
||||
err = read_modbus_parameter(mbm_handle, CID_DEV_REG1, &holding_registers[CID_DEV_REG1]);
|
||||
err = test_common_read_modbus_parameter(mbm_handle, CID_DEV_REG1, &holding_registers[CID_DEV_REG1]);
|
||||
CHECK_PAR_VALUE(CID_DEV_REG1, err, holding_registers[CID_DEV_REG1], TEST_REG_VAL2);
|
||||
|
||||
err = read_modbus_parameter(mbm_handle, CID_DEV_REG2, &holding_registers[CID_DEV_REG2]);
|
||||
err = test_common_read_modbus_parameter(mbm_handle, CID_DEV_REG2, &holding_registers[CID_DEV_REG2]);
|
||||
CHECK_PAR_VALUE(CID_DEV_REG2, err, holding_registers[CID_DEV_REG2], TEST_REG_VAL3);
|
||||
|
||||
err = read_modbus_parameter(mbm_handle, CID_DEV_REG3, &holding_registers[CID_DEV_REG3]);
|
||||
err = test_common_read_modbus_parameter(mbm_handle, CID_DEV_REG3, &holding_registers[CID_DEV_REG3]);
|
||||
CHECK_PAR_VALUE(CID_DEV_REG3, err, holding_registers[CID_DEV_REG3], TEST_REG_VAL4);
|
||||
req_type = RT_HOLDING_WR;
|
||||
break;
|
||||
@ -344,16 +475,17 @@ static void test_master_task(void *arg)
|
||||
default:
|
||||
break;
|
||||
}
|
||||
write_modbus_parameter(mbm_handle, CID_DEV_REG_COUNT, &cycle_counter);
|
||||
vTaskDelay(TEST_TASK_TICK_TIME); // Let the IDLE task to trigger
|
||||
if (holding_registers[CID_DEV_REG_COUNT] >= TEST_TASK_CYCLE_COUNTER) {
|
||||
ESP_LOGI(TAG, "Stop master: %p.", mbm_handle);
|
||||
break;
|
||||
} else {
|
||||
write_modbus_parameter(mbm_handle, CID_DEV_REG_COUNT, &cycle_counter);
|
||||
vTaskDelay(TEST_TASK_TICK_TIME); // Let the IDLE task to trigger
|
||||
}
|
||||
}
|
||||
ESP_LOGI(TAG, "Destroy master, inst: %p.", mbm_handle);
|
||||
TEST_ESP_OK(mbc_master_delete(mbm_handle));
|
||||
test_comon_notify_done((uint32_t)xTaskGetCurrentTaskHandle());
|
||||
test_common_task_notify_done(xTaskGetCurrentTaskHandle());
|
||||
vTaskSuspend(NULL);
|
||||
}
|
||||
|
||||
@ -387,18 +519,20 @@ static void test_slave_task(void *arg)
|
||||
}
|
||||
vTaskDelay(TEST_TASK_TICK_TIME); // Let IDLE task to trigger
|
||||
if (holding_registers[CID_DEV_REG_COUNT] >= TEST_TASK_CYCLE_COUNTER) {
|
||||
ESP_LOGI(TAG, "Stop slave: %p.", mbs_handle);
|
||||
ESP_LOGD(TAG, "Stop slave: %p.", mbs_handle);
|
||||
vTaskDelay(TEST_TASK_TICK_TIME); // Let master to get response from slave prior to close
|
||||
break;
|
||||
}
|
||||
}
|
||||
ESP_LOGI(TAG, "Destroy slave, inst: %p.", mbs_handle);
|
||||
TEST_ESP_OK(mbc_slave_delete(mbs_handle));
|
||||
test_comon_notify_done((uint32_t)xTaskGetCurrentTaskHandle());
|
||||
ESP_LOGD(TAG, "Notify task done, inst: %p.", xTaskGetCurrentTaskHandle());
|
||||
test_common_task_notify_done(xTaskGetCurrentTaskHandle());
|
||||
vTaskDelay(10);
|
||||
vTaskSuspend(NULL);
|
||||
}
|
||||
|
||||
void test_slave_setup_start(void *mbs_handle)
|
||||
void test_common_slave_setup_start(void *mbs_handle)
|
||||
{
|
||||
TEST_ASSERT_TRUE(mbs_handle);
|
||||
mb_register_area_descriptor_t reg_area;
|
||||
@ -425,7 +559,10 @@ void test_slave_setup_start(void *mbs_handle)
|
||||
|
||||
#if (CONFIG_FMB_COMM_MODE_RTU_EN || CONFIG_FMB_COMM_MODE_ASCII_EN)
|
||||
|
||||
TaskHandle_t test_master_serial_create(mb_communication_info_t *pconfig, const mb_parameter_descriptor_t *pdescr, uint16_t descr_size)
|
||||
TaskHandle_t test_common_master_serial_create(mb_communication_info_t *pconfig,
|
||||
uint32_t priority,
|
||||
const mb_parameter_descriptor_t *pdescr,
|
||||
uint16_t descr_size)
|
||||
{
|
||||
if (!pconfig || !pdescr) {
|
||||
ESP_LOGI(TAG, "invalid master configuration.");
|
||||
@ -442,16 +579,21 @@ TaskHandle_t test_master_serial_create(mb_communication_info_t *pconfig, const m
|
||||
|
||||
TEST_ESP_OK(mbc_master_start(mbm_handle));
|
||||
ESP_LOGI(TAG, "%p, modbus master start...", mbm_handle) ;
|
||||
|
||||
if (priority) {
|
||||
priority = TEST_TASK_PRIO_MASTER;
|
||||
}
|
||||
|
||||
char* port_name = pbase->mb_base->descr.parent_name;
|
||||
TEST_ASSERT_TRUE(xTaskCreatePinnedToCore(test_master_task, port_name,
|
||||
TEST_TASK_STACK_SIZE,
|
||||
mbm_handle, (TEST_TASK_PRIO_MASTER),
|
||||
mbm_handle, priority,
|
||||
&master_task_handle, MB_PORT_TASK_AFFINITY));
|
||||
test_task_add_entry(master_task_handle, mbm_handle);
|
||||
return master_task_handle;
|
||||
}
|
||||
|
||||
TaskHandle_t test_slave_serial_create(mb_communication_info_t *pconfig)
|
||||
TaskHandle_t test_common_slave_serial_create(mb_communication_info_t *pconfig, uint32_t priority)
|
||||
{
|
||||
if (!pconfig) {
|
||||
ESP_LOGI(TAG, "invalid slave configuration.");
|
||||
@ -464,12 +606,17 @@ TaskHandle_t test_slave_serial_create(mb_communication_info_t *pconfig)
|
||||
|
||||
mbs_controller_iface_t *pbase = mbs_handle;
|
||||
|
||||
test_slave_setup_start(mbs_handle);
|
||||
test_common_slave_setup_start(mbs_handle);
|
||||
|
||||
if (priority) {
|
||||
priority = TEST_TASK_PRIO_SLAVE;
|
||||
}
|
||||
|
||||
TEST_ASSERT_TRUE(xTaskCreatePinnedToCore(test_slave_task, pbase->mb_base->descr.parent_name,
|
||||
TEST_TASK_STACK_SIZE,
|
||||
mbs_handle, (TEST_TASK_PRIO_SLAVE),
|
||||
mbs_handle, priority,
|
||||
&slave_task_handle, MB_PORT_TASK_AFFINITY));
|
||||
test_task_add_entry(slave_task_handle, mbs_handle);
|
||||
return slave_task_handle;
|
||||
}
|
||||
|
||||
@ -477,7 +624,7 @@ TaskHandle_t test_slave_serial_create(mb_communication_info_t *pconfig)
|
||||
|
||||
#if (CONFIG_FMB_COMM_MODE_TCP_EN)
|
||||
|
||||
TaskHandle_t test_master_tcp_create(mb_communication_info_t *pconfig, const mb_parameter_descriptor_t *pdescr, uint16_t descr_size)
|
||||
TaskHandle_t test_common_master_tcp_create(mb_communication_info_t *pconfig, uint32_t priority, const mb_parameter_descriptor_t *pdescr, uint16_t descr_size)
|
||||
{
|
||||
if (!pconfig || !pdescr) {
|
||||
ESP_LOGI(TAG, "invalid master configuration.");
|
||||
@ -495,15 +642,21 @@ TaskHandle_t test_master_tcp_create(mb_communication_info_t *pconfig, const mb_p
|
||||
TEST_ESP_OK(mbc_master_start(mbm_handle));
|
||||
ESP_LOGI(TAG, "%p, modbus master start...", mbm_handle) ;
|
||||
|
||||
if (priority) {
|
||||
priority = TEST_TASK_PRIO_MASTER;
|
||||
}
|
||||
|
||||
char *port_name = pbase->mb_base->descr.parent_name;
|
||||
TEST_ASSERT_TRUE(xTaskCreatePinnedToCore(test_master_task, port_name,
|
||||
TEST_TASK_STACK_SIZE,
|
||||
mbm_handle, (TEST_TASK_PRIO_MASTER),
|
||||
mbm_handle, priority,
|
||||
&master_task_handle, MB_PORT_TASK_AFFINITY));
|
||||
|
||||
test_task_add_entry(master_task_handle, mbm_handle);
|
||||
return master_task_handle;
|
||||
}
|
||||
|
||||
TaskHandle_t test_slave_tcp_create(mb_communication_info_t *pconfig)
|
||||
TaskHandle_t test_common_slave_tcp_create(mb_communication_info_t *pconfig, uint32_t priority)
|
||||
{
|
||||
if (!pconfig) {
|
||||
ESP_LOGI(TAG, "invalid slave configuration.");
|
||||
@ -515,12 +668,17 @@ TaskHandle_t test_slave_tcp_create(mb_communication_info_t *pconfig)
|
||||
TEST_ESP_OK(mbc_slave_create_tcp(pconfig, &mbs_handle));
|
||||
|
||||
mbs_controller_iface_t *pbase = mbs_handle;
|
||||
test_slave_setup_start(mbs_handle);
|
||||
test_common_slave_setup_start(mbs_handle);
|
||||
|
||||
if (priority) {
|
||||
priority = TEST_TASK_PRIO_SLAVE;
|
||||
}
|
||||
|
||||
TEST_ASSERT_TRUE(xTaskCreatePinnedToCore(test_slave_task, pbase->mb_base->descr.parent_name,
|
||||
TEST_TASK_STACK_SIZE,
|
||||
mbs_handle, (TEST_TASK_PRIO_SLAVE),
|
||||
mbs_handle, priority,
|
||||
&slave_task_handle, MB_PORT_TASK_AFFINITY));
|
||||
test_task_add_entry(slave_task_handle, mbs_handle);
|
||||
return slave_task_handle;
|
||||
}
|
||||
|
||||
|
@ -22,11 +22,11 @@ extern "C" {
|
||||
typedef struct mb_base_t mb_base_t; /*!< Type of modbus object */
|
||||
typedef struct mb_port_base_t mb_port_base_t;
|
||||
|
||||
bool mb_port_evt_get(mb_port_base_t *inst, mb_event_t *pevent);
|
||||
bool mb_port_evt_post(mb_port_base_t *inst, mb_event_t event);
|
||||
bool mb_port_evt_res_take(mb_port_base_t *inst, uint32_t timeout);
|
||||
void mb_port_evt_res_release(mb_port_base_t *inst);
|
||||
mb_err_enum_t mb_port_evt_wait_req_finish(mb_port_base_t *inst);
|
||||
bool mb_port_event_get(mb_port_base_t *inst, mb_event_t *pevent);
|
||||
bool mb_port_event_post(mb_port_base_t *inst, mb_event_t event);
|
||||
bool mb_port_event_res_take(mb_port_base_t *inst, uint32_t timeout);
|
||||
void mb_port_event_res_release(mb_port_base_t *inst);
|
||||
mb_err_enum_t mb_port_event_wait_req_finish(mb_port_base_t *inst);
|
||||
|
||||
#if (CONFIG_FMB_COMM_MODE_ASCII_EN || CONFIG_FMB_COMM_MODE_RTU_EN)
|
||||
|
||||
@ -91,6 +91,10 @@ mb_exception_t mbs_fn_rw_multi_holding_reg(mb_base_t *inst, uint8_t *frame_ptr,u
|
||||
|
||||
#endif
|
||||
|
||||
#if (CONFIG_FMB_COMM_MODE_TCP_EN)
|
||||
|
||||
typedef struct _port_tcp_opts mb_tcp_opts_t;
|
||||
|
||||
mb_err_enum_t mbs_tcp_create(mb_tcp_opts_t *tcp_opts, void **in_out_obj);
|
||||
|
||||
mb_err_enum_t mbs_delete(mb_base_t *inst);
|
||||
@ -99,11 +103,8 @@ mb_err_enum_t mbs_disable(mb_base_t *inst);
|
||||
mb_err_enum_t mbs_poll(mb_base_t *inst);
|
||||
mb_err_enum_t mbs_set_slv_id(mb_base_t *inst, uint8_t slv_id, bool is_running, uint8_t const *slv_idstr, uint16_t slv_idstr_len);
|
||||
|
||||
#if (CONFIG_FMB_COMM_MODE_TCP_EN)
|
||||
|
||||
typedef struct _port_tcp_opts mb_tcp_opts_t;
|
||||
|
||||
mb_err_enum_t mbm_tcp_create(mb_tcp_opts_t *tcp_opts, void **in_out_obj);
|
||||
mb_uid_info_t *mbm_port_tcp_get_slave_info(mb_port_base_t *inst, uint8_t slave_addr, mb_sock_state_t exp_state);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
dependencies:
|
||||
idf: ">=4.3"
|
||||
espressif/esp-modbus:
|
||||
version: "^2.0"
|
||||
version: "^2.0.0-beta"
|
||||
override_path: "../../../../"
|
||||
|
||||
|
@ -23,10 +23,10 @@
|
||||
|
||||
#define TEST_MASTER_RESPOND_TOUT_MS CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND
|
||||
|
||||
#define TAG "MODBUS_SERIAL_TEST"
|
||||
#define TAG "MODBUS_CONTROLLER_COMMON_TEST"
|
||||
|
||||
// The workaround to statically link whole test library
|
||||
__attribute__((unused)) bool mb_test_include_impl = 1;
|
||||
__attribute__((unused)) bool mb_test_include_impl = true;
|
||||
|
||||
enum {
|
||||
CID_DEV_REG0_INPUT,
|
||||
@ -34,7 +34,8 @@ enum {
|
||||
CID_DEV_REG1_INPUT,
|
||||
CID_DEV_REG0_COIL,
|
||||
CID_DEV_REG0_DISCRITE,
|
||||
CID_DEV_REG_CNT
|
||||
CID_DEV_REG_CNT,
|
||||
CID_ITEMS_CNT
|
||||
};
|
||||
|
||||
// Example Data (Object) Dictionary for Modbus parameters
|
||||
@ -68,7 +69,42 @@ TEST_TEAR_DOWN(unit_test_controller)
|
||||
test_common_stop();
|
||||
}
|
||||
|
||||
TEST(unit_test_controller, test_setup_destroy_master)
|
||||
#if (CONFIG_FMB_COMM_MODE_TCP_EN)
|
||||
|
||||
TEST(unit_test_controller, test_setup_destroy_master_tcp)
|
||||
{
|
||||
mb_communication_info_t master_config = {
|
||||
.tcp_opts.port = TEST_TCP_PORT_NUM,
|
||||
.tcp_opts.mode = MB_TCP,
|
||||
.tcp_opts.addr_type = MB_IPV4,
|
||||
.tcp_opts.ip_addr_table = (void *)(0x44332211),
|
||||
.tcp_opts.uid = MB_DEVICE_ADDR1,
|
||||
.tcp_opts.start_disconnected = true,
|
||||
.tcp_opts.response_tout_ms = 1,
|
||||
.tcp_opts.test_tout_us = TEST_SLAVE_SEND_TOUT_US,
|
||||
.tcp_opts.ip_netif_ptr = (void *)(0x11223344)
|
||||
};
|
||||
|
||||
ESP_LOGI(TAG, "TEST: Verify master create-destroy sequence TCP.");
|
||||
|
||||
void *mbm_handle = NULL;
|
||||
mb_base_t *pmb_base = NULL;
|
||||
TEST_ESP_ERR(MB_ENOERR, mb_stub_tcp_create(&master_config.tcp_opts, (void *)&pmb_base));
|
||||
|
||||
mbm_tcp_create_ExpectAnyArgsAndReturn(MB_ENOERR);
|
||||
mbm_tcp_create_ReturnThruPtr_in_out_obj((void **)&pmb_base);
|
||||
mbm_port_tcp_get_slave_info_IgnoreAndReturn((void *)(0x11223344));
|
||||
TEST_ESP_OK(mbc_master_create_tcp(&master_config, &mbm_handle));
|
||||
TEST_ESP_OK(mbc_master_set_descriptor(mbm_handle, &descriptors[0], num_descriptors));
|
||||
TEST_ESP_OK(mbc_master_delete(mbm_handle));
|
||||
ESP_LOGI(TAG, "Test passed successfully.");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if (CONFIG_FMB_COMM_MODE_RTU_EN || CONFIG_FMB_COMM_MODE_ASCII_EN)
|
||||
|
||||
TEST(unit_test_controller, test_setup_destroy_master_serial)
|
||||
{
|
||||
mb_communication_info_t master_config = {
|
||||
.ser_opts.port = TEST_SER_PORT_NUM,
|
||||
@ -86,7 +122,7 @@ TEST(unit_test_controller, test_setup_destroy_master)
|
||||
|
||||
void *mbm_handle = NULL;
|
||||
mb_base_t *pmb_base = NULL;
|
||||
TEST_ESP_ERR(MB_ENOERR, mb_stub_create(&master_config.ser_opts, (void *)&pmb_base));
|
||||
TEST_ESP_ERR(MB_ENOERR, mb_stub_serial_create(&master_config.ser_opts, (void *)&pmb_base));
|
||||
|
||||
mbm_rtu_create_ExpectAnyArgsAndReturn(MB_ENOERR);
|
||||
mbm_rtu_create_ReturnThruPtr_in_out_obj((void **)&pmb_base);
|
||||
@ -105,7 +141,7 @@ TEST(unit_test_controller, test_setup_destroy_master)
|
||||
ESP_LOGI(TAG, "Test passed successfully.");
|
||||
}
|
||||
|
||||
TEST(unit_test_controller, test_setup_destroy_slave)
|
||||
TEST(unit_test_controller, test_setup_destroy_slave_serial)
|
||||
{
|
||||
// Initialize and start Modbus controller
|
||||
mb_communication_info_t slave_config = {
|
||||
@ -122,7 +158,7 @@ TEST(unit_test_controller, test_setup_destroy_slave)
|
||||
ESP_LOGI(TAG, "TEST: Verify slave create-destroy sequence.");
|
||||
void *mbs_handle = NULL;
|
||||
mb_base_t *pmb_base = mbs_handle;
|
||||
TEST_ESP_ERR(MB_ENOERR, mb_stub_create(&slave_config.ser_opts, (void *)&pmb_base));
|
||||
TEST_ESP_ERR(MB_ENOERR, mb_stub_serial_create(&slave_config.ser_opts, (void *)&pmb_base));
|
||||
mbs_rtu_create_ExpectAndReturn(&slave_config.ser_opts, (void *)pmb_base, MB_ENOERR);
|
||||
mbs_rtu_create_IgnoreArg_in_out_obj();
|
||||
mbs_rtu_create_ReturnThruPtr_in_out_obj((void **)&pmb_base);
|
||||
@ -153,14 +189,14 @@ esp_err_t test_master_registers(int par_index, mb_err_enum_t mb_err)
|
||||
mb_base_t *pmb_base = NULL; // fake mb_base handle
|
||||
void *mbm_handle = NULL;
|
||||
|
||||
TEST_ESP_ERR(MB_ENOERR, mb_stub_create(&master_config.ser_opts, (void *)&pmb_base));
|
||||
TEST_ESP_ERR(MB_ENOERR, mb_stub_serial_create(&master_config.ser_opts, (void *)&pmb_base));
|
||||
pmb_base->port_obj = (mb_port_base_t *)0x44556677;
|
||||
mbm_rtu_create_ExpectAnyArgsAndReturn(MB_ENOERR);
|
||||
mbm_rtu_create_ReturnThruPtr_in_out_obj((void **)&pmb_base);
|
||||
TEST_ESP_OK(mbc_master_create_serial(&master_config, &mbm_handle));
|
||||
TEST_ESP_OK(mbc_master_set_descriptor(mbm_handle, &descriptors[0], num_descriptors));
|
||||
mb_port_evt_res_take_ExpectAnyArgsAndReturn(true);
|
||||
mb_port_evt_res_release_ExpectAnyArgs();
|
||||
mb_port_event_res_take_ExpectAnyArgsAndReturn(true);
|
||||
mb_port_event_res_release_ExpectAnyArgs();
|
||||
TEST_ESP_OK(mbc_master_start(mbm_handle));
|
||||
|
||||
const mb_parameter_descriptor_t *param_descriptor = NULL;
|
||||
@ -225,7 +261,7 @@ esp_err_t test_master_registers(int par_index, mb_err_enum_t mb_err)
|
||||
// Check if modbus controller object forms correct modbus request from data dictionary
|
||||
// and is able to transfer data using mb_object. Check possible errors returned back from
|
||||
// mb_object and make sure the modbus controller handles them correctly.
|
||||
TEST(unit_test_controller, test_master_send_request)
|
||||
TEST(unit_test_controller, test_master_send_request_serial)
|
||||
{
|
||||
TEST_ESP_ERR(ESP_OK, test_master_registers(CID_DEV_REG0_INPUT, MB_ENOERR));
|
||||
TEST_ESP_ERR(ESP_ERR_TIMEOUT, test_master_registers(CID_DEV_REG0_INPUT, MB_ETIMEDOUT));
|
||||
@ -237,9 +273,19 @@ TEST(unit_test_controller, test_master_send_request)
|
||||
TEST_ESP_ERR(ESP_ERR_TIMEOUT, test_master_registers(CID_DEV_REG0_DISCRITE, MB_ETIMEDOUT));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
TEST_GROUP_RUNNER(unit_test_controller)
|
||||
{
|
||||
RUN_TEST_CASE(unit_test_controller, test_setup_destroy_master);
|
||||
RUN_TEST_CASE(unit_test_controller, test_setup_destroy_slave);
|
||||
RUN_TEST_CASE(unit_test_controller, test_master_send_request);
|
||||
|
||||
#if (CONFIG_FMB_COMM_MODE_RTU_EN || CONFIG_FMB_COMM_MODE_ASCII_EN)
|
||||
RUN_TEST_CASE(unit_test_controller, test_setup_destroy_master_serial);
|
||||
RUN_TEST_CASE(unit_test_controller, test_setup_destroy_slave_serial);
|
||||
RUN_TEST_CASE(unit_test_controller, test_master_send_request_serial);
|
||||
#endif
|
||||
|
||||
#if (CONFIG_FMB_COMM_MODE_TCP_EN)
|
||||
RUN_TEST_CASE(unit_test_controller, test_setup_destroy_master_tcp);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
@ -6,11 +6,11 @@ from pytest_embedded import Dut
|
||||
|
||||
|
||||
CONFIGS = [
|
||||
pytest.param('serial', marks=[pytest.mark.esp32, pytest.mark.esp32s2, pytest.mark.esp32s3, pytest.mark.esp32c3]),
|
||||
pytest.param('generic', marks=[pytest.mark.esp32, pytest.mark.esp32s2, pytest.mark.esp32s3, pytest.mark.esp32c3]),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.generic_multi_device
|
||||
@pytest.mark.multi_dut_modbus_generic
|
||||
@pytest.mark.parametrize('config', CONFIGS, indirect=True)
|
||||
def test_modbus_controller_common(dut: Dut) -> None:
|
||||
dut.expect_unity_test_output()
|
||||
|
@ -9,12 +9,12 @@ CONFIG_FMB_PORT_TASK_STACK_SIZE=4096
|
||||
CONFIG_FMB_PORT_TASK_PRIO=10
|
||||
CONFIG_FMB_COMM_MODE_RTU_EN=y
|
||||
CONFIG_FMB_COMM_MODE_ASCII_EN=y
|
||||
CONFIG_FMB_COMM_MODE_TCP_EN=n
|
||||
CONFIG_FMB_COMM_MODE_TCP_EN=y
|
||||
CONFIG_FMB_TCP_UID_ENABLED=y
|
||||
CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=2000
|
||||
CONFIG_FMB_MASTER_DELAY_MS_CONVERT=300
|
||||
CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y
|
||||
CONFIG_MB_PORT_ADAPTER_EN=y
|
||||
CONFIG_MB_PORT_ADAPTER_EN=n
|
||||
CONFIG_MB_TEST_SLAVE_TASK_PRIO=4
|
||||
CONFIG_MB_TEST_MASTER_TASK_PRIO=4
|
||||
CONFIG_MB_TEST_COMM_CYCLE_COUNTER=10
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user