Additional tests
This commit is contained in:
parent
c064e47368
commit
13cd7c74b6
61
modbusino.c
61
modbusino.c
@ -1,7 +1,7 @@
|
||||
#include "modbusino.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef MBSN_DEBUG
|
||||
#include <stdio.h>
|
||||
@ -43,18 +43,6 @@
|
||||
(m)->msg.buf_idx += 2
|
||||
#endif
|
||||
|
||||
/*
|
||||
static msg_state msg_state_create() {
|
||||
msg_state s;
|
||||
memset(&s, 0, sizeof(msg_state));
|
||||
return s;
|
||||
}
|
||||
|
||||
static msg_state res_from_req(msg_state* req) {
|
||||
msg_state res = *req;
|
||||
return res;
|
||||
}
|
||||
*/
|
||||
|
||||
static void msg_buf_reset(mbsn_t* mbsn) {
|
||||
mbsn->msg.buf_idx = 0;
|
||||
@ -93,6 +81,7 @@ int mbsn_create(mbsn_t* mbsn, const mbsn_platform_conf* platform_conf) {
|
||||
|
||||
mbsn->byte_timeout_ms = -1;
|
||||
mbsn->read_timeout_ms = -1;
|
||||
mbsn->byte_spacing_ms = 0;
|
||||
|
||||
if (!platform_conf)
|
||||
return MBSN_ERROR_INVALID_ARGUMENT;
|
||||
@ -105,7 +94,6 @@ int mbsn_create(mbsn_t* mbsn, const mbsn_platform_conf* platform_conf) {
|
||||
|
||||
mbsn->platform = *platform_conf;
|
||||
|
||||
|
||||
return MBSN_ERROR_NONE;
|
||||
}
|
||||
|
||||
@ -141,6 +129,11 @@ void mbsn_set_byte_timeout(mbsn_t* mbsn, int32_t timeout_ms) {
|
||||
}
|
||||
|
||||
|
||||
void mbsn_set_byte_spacing(mbsn_t* mbsn, uint32_t spacing_ms) {
|
||||
mbsn->byte_spacing_ms = spacing_ms;
|
||||
}
|
||||
|
||||
|
||||
void mbsn_set_destination_rtu_address(mbsn_t* mbsn, uint8_t address) {
|
||||
mbsn->dest_address_rtu = address;
|
||||
}
|
||||
@ -196,7 +189,14 @@ static mbsn_error recv(mbsn_t* mbsn, uint32_t count) {
|
||||
|
||||
|
||||
static mbsn_error send(mbsn_t* mbsn) {
|
||||
uint32_t spacing_ms = 0;
|
||||
if (mbsn->platform.transport == MBSN_TRANSPORT_RTU)
|
||||
spacing_ms = mbsn->byte_spacing_ms;
|
||||
|
||||
for (int i = 0; i < mbsn->msg.buf_idx; i++) {
|
||||
if (spacing_ms != 0)
|
||||
mbsn->platform.sleep(spacing_ms);
|
||||
|
||||
int ret = mbsn->platform.write_byte(mbsn->msg.buf[i], mbsn->read_timeout_ms);
|
||||
if (ret == 0) {
|
||||
return MBSN_ERROR_TIMEOUT;
|
||||
@ -539,10 +539,10 @@ static mbsn_error handle_write_single_coil(mbsn_t* mbsn) {
|
||||
return err;
|
||||
|
||||
if (!mbsn->msg.ignored) {
|
||||
if (mbsn->callbacks.write_single_coil) {
|
||||
if (value != 0 && value != 0xFF00)
|
||||
return handle_exception(mbsn, MBSN_EXCEPTION_ILLEGAL_DATA_VALUE);
|
||||
|
||||
if (mbsn->callbacks.write_single_coil) {
|
||||
err = mbsn->callbacks.write_single_coil(address, value == 0 ? false : true);
|
||||
if (err != MBSN_ERROR_NONE) {
|
||||
if (mbsn_error_is_exception(err))
|
||||
@ -552,8 +552,9 @@ static mbsn_error handle_write_single_coil(mbsn_t* mbsn) {
|
||||
}
|
||||
|
||||
if (!mbsn->msg.broadcast) {
|
||||
send_msg_header(mbsn, 2);
|
||||
send_msg_header(mbsn, 4);
|
||||
put_2(mbsn, address);
|
||||
put_2(mbsn, value);
|
||||
err = send_msg_footer(mbsn);
|
||||
if (err != MBSN_ERROR_NONE)
|
||||
return err;
|
||||
@ -591,7 +592,8 @@ static mbsn_error handle_write_single_register(mbsn_t* mbsn) {
|
||||
}
|
||||
|
||||
if (!mbsn->msg.broadcast) {
|
||||
send_msg_header(mbsn, 1);
|
||||
send_msg_header(mbsn, 4);
|
||||
put_2(mbsn, address);
|
||||
put_2(mbsn, value);
|
||||
err = send_msg_footer(mbsn);
|
||||
if (err != MBSN_ERROR_NONE)
|
||||
@ -621,8 +623,9 @@ static mbsn_error handle_write_multiple_coils(mbsn_t* mbsn) {
|
||||
return err;
|
||||
|
||||
mbsn_bitfield coils;
|
||||
for (int i = 0; i < coils_bytes; i++)
|
||||
for (int i = 0; i < coils_bytes; i++) {
|
||||
coils[i] = get_1(mbsn);
|
||||
}
|
||||
|
||||
err = recv_msg_footer(mbsn);
|
||||
if (err != MBSN_ERROR_NONE)
|
||||
@ -682,7 +685,7 @@ static mbsn_error handle_write_multiple_registers(mbsn_t* mbsn) {
|
||||
return err;
|
||||
|
||||
uint16_t registers[0x007B];
|
||||
for (int i = 0; i < quantity; i++) {
|
||||
for (int i = 0; i < registers_bytes / 2; i++) {
|
||||
registers[i] = get_2(mbsn);
|
||||
}
|
||||
|
||||
@ -865,9 +868,6 @@ mbsn_error mbsn_server_receive(mbsn_t* mbsn) {
|
||||
|
||||
|
||||
static mbsn_error read_discrete(mbsn_t* mbsn, uint8_t fc, uint16_t address, uint16_t quantity, mbsn_bitfield values) {
|
||||
if (address == MBSN_BROADCAST_ADDRESS)
|
||||
return MBSN_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
if (quantity < 1 || quantity > 2000)
|
||||
return MBSN_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
@ -920,10 +920,7 @@ mbsn_error mbsn_read_discrete_inputs(mbsn_t* mbsn, uint16_t address, uint16_t qu
|
||||
}
|
||||
|
||||
|
||||
mbsn_error read_registers(mbsn_t* mbsn, uint8_t fc, uint16_t address, uint16_t quantity, uint16_t* registers) {
|
||||
if (address == MBSN_BROADCAST_ADDRESS)
|
||||
return MBSN_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
static mbsn_error read_registers(mbsn_t* mbsn, uint8_t fc, uint16_t address, uint16_t quantity, uint16_t* registers) {
|
||||
if (quantity < 1 || quantity > 125)
|
||||
return MBSN_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
@ -983,8 +980,10 @@ mbsn_error mbsn_write_single_coil(mbsn_t* mbsn, uint16_t address, bool value) {
|
||||
msg_state_req(mbsn, 5);
|
||||
send_msg_header(mbsn, 4);
|
||||
|
||||
uint16_t value_req = value ? 0xFF00 : 0;
|
||||
|
||||
put_2(mbsn, address);
|
||||
put_2(mbsn, value ? 0xFF00 : 0);
|
||||
put_2(mbsn, value_req);
|
||||
|
||||
mbsn_error err = send_msg_footer(mbsn);
|
||||
if (err != MBSN_ERROR_NONE)
|
||||
@ -1009,7 +1008,7 @@ mbsn_error mbsn_write_single_coil(mbsn_t* mbsn, uint16_t address, bool value) {
|
||||
if (address_res != address)
|
||||
return MBSN_ERROR_INVALID_RESPONSE;
|
||||
|
||||
if (value_res != value)
|
||||
if (value_res != value_req)
|
||||
return MBSN_ERROR_INVALID_RESPONSE;
|
||||
}
|
||||
|
||||
@ -1018,7 +1017,7 @@ mbsn_error mbsn_write_single_coil(mbsn_t* mbsn, uint16_t address, bool value) {
|
||||
|
||||
|
||||
mbsn_error mbsn_write_single_register(mbsn_t* mbsn, uint16_t address, uint16_t value) {
|
||||
msg_state_req(mbsn, 5);
|
||||
msg_state_req(mbsn, 6);
|
||||
send_msg_header(mbsn, 4);
|
||||
|
||||
put_2(mbsn, address);
|
||||
@ -1056,7 +1055,7 @@ mbsn_error mbsn_write_single_register(mbsn_t* mbsn, uint16_t address, uint16_t v
|
||||
|
||||
|
||||
mbsn_error mbsn_write_multiple_coils(mbsn_t* mbsn, uint16_t address, uint16_t quantity, const mbsn_bitfield coils) {
|
||||
if (quantity < 0 || quantity > 0x07B0)
|
||||
if (quantity < 1 || quantity > 0x07B0)
|
||||
return MBSN_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
if ((uint32_t) address + (uint32_t) quantity > 0xFFFF + 1)
|
||||
@ -1107,7 +1106,7 @@ mbsn_error mbsn_write_multiple_coils(mbsn_t* mbsn, uint16_t address, uint16_t qu
|
||||
|
||||
|
||||
mbsn_error mbsn_write_multiple_registers(mbsn_t* mbsn, uint16_t address, uint16_t quantity, const uint16_t* registers) {
|
||||
if (quantity < 0 || quantity > 0x007B)
|
||||
if (quantity < 1 || quantity > 0x007B)
|
||||
return MBSN_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
if ((uint32_t) address + (uint32_t) quantity > 0xFFFF + 1)
|
||||
|
||||
@ -51,8 +51,8 @@ typedef struct mbsn_callbacks {
|
||||
mbsn_error (*read_input_registers)(uint16_t address, uint16_t quantity, uint16_t* registers_out);
|
||||
mbsn_error (*write_single_coil)(uint16_t address, bool value);
|
||||
mbsn_error (*write_single_register)(uint16_t address, uint16_t value);
|
||||
mbsn_error (*write_multiple_coils)(uint16_t address, uint16_t quantity, mbsn_bitfield coils);
|
||||
mbsn_error (*write_multiple_registers)(uint16_t address, uint16_t quantity, uint16_t* registers);
|
||||
mbsn_error (*write_multiple_coils)(uint16_t address, uint16_t quantity, const mbsn_bitfield coils);
|
||||
mbsn_error (*write_multiple_registers)(uint16_t address, uint16_t quantity, const uint16_t* registers);
|
||||
} mbsn_callbacks;
|
||||
|
||||
|
||||
@ -72,6 +72,7 @@ typedef struct mbsn_t {
|
||||
|
||||
int32_t byte_timeout_ms;
|
||||
int32_t read_timeout_ms;
|
||||
uint32_t byte_spacing_ms;
|
||||
|
||||
mbsn_platform_conf platform;
|
||||
|
||||
|
||||
@ -2,11 +2,12 @@ cmake_minimum_required(VERSION 3.21)
|
||||
project(modbusino_tests C)
|
||||
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0 -g")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3")
|
||||
|
||||
include_directories(. ..)
|
||||
|
||||
add_definitions(-DMBSN_DEBUG)
|
||||
#add_definitions(-DMBSN_DEBUG)
|
||||
|
||||
add_executable(modbusino_tests ../modbusino.c modbusino_tests.c)
|
||||
|
||||
|
||||
@ -153,17 +153,31 @@ mbsn_error read_discrete(uint16_t address, uint16_t quantity, mbsn_bitfield coil
|
||||
mbsn_bitfield_write(coils_out, 2, 1);
|
||||
}
|
||||
|
||||
if (address == 65526 && quantity == 10) {
|
||||
mbsn_bitfield_write(coils_out, 0, 1);
|
||||
mbsn_bitfield_write(coils_out, 1, 0);
|
||||
mbsn_bitfield_write(coils_out, 2, 1);
|
||||
mbsn_bitfield_write(coils_out, 3, 0);
|
||||
mbsn_bitfield_write(coils_out, 4, 1);
|
||||
mbsn_bitfield_write(coils_out, 5, 0);
|
||||
mbsn_bitfield_write(coils_out, 6, 1);
|
||||
mbsn_bitfield_write(coils_out, 7, 0);
|
||||
mbsn_bitfield_write(coils_out, 8, 1);
|
||||
mbsn_bitfield_write(coils_out, 9, 0);
|
||||
}
|
||||
|
||||
return MBSN_ERROR_NONE;
|
||||
}
|
||||
|
||||
|
||||
void test_fc1(mbsn_transport transport) {
|
||||
const uint8_t fc = 1;
|
||||
uint8_t raw_res[260];
|
||||
|
||||
start_client_and_server(transport, (mbsn_callbacks){});
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_FUNCTION when callback is not registered server-side");
|
||||
assert(mbsn_read_coils(&CLIENT, 1, 1, NULL) == MBSN_EXCEPTION_ILLEGAL_FUNCTION);
|
||||
assert(mbsn_read_coils(&CLIENT, 0, 1, NULL) == MBSN_EXCEPTION_ILLEGAL_FUNCTION);
|
||||
|
||||
stop_client_and_server();
|
||||
|
||||
@ -175,19 +189,19 @@ void test_fc1(mbsn_transport transport) {
|
||||
should("immediately return MBSN_ERROR_INVALID_ARGUMENT when calling with quantity > 2000");
|
||||
assert(mbsn_read_coils(&CLIENT, 1, 2001, NULL) == MBSN_ERROR_INVALID_ARGUMENT);
|
||||
|
||||
should("immediately return MBSN_ERROR_INVALID_ARGUMENT when calling with address + quantity > 65535");
|
||||
should("immediately return MBSN_ERROR_INVALID_ARGUMENT when calling with address + quantity > 0xFFFF + 1");
|
||||
assert(mbsn_read_coils(&CLIENT, 65530, 7, NULL) == MBSN_ERROR_INVALID_ARGUMENT);
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_DATA_VALUE from server when calling with quantity 0");
|
||||
check(mbsn_send_raw_pdu(&CLIENT, 1, (uint16_t[]){htons(1), htons(0)}, 4));
|
||||
check(mbsn_send_raw_pdu(&CLIENT, fc, (uint16_t[]){htons(1), htons(0)}, 4));
|
||||
assert(mbsn_receive_raw_pdu_response(&CLIENT, raw_res, 2) == MBSN_EXCEPTION_ILLEGAL_DATA_VALUE);
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_DATA_VALUE from server when calling with quantity > 2000");
|
||||
check(mbsn_send_raw_pdu(&CLIENT, 1, (uint16_t[]){htons(1), htons(2001)}, 4));
|
||||
check(mbsn_send_raw_pdu(&CLIENT, fc, (uint16_t[]){htons(1), htons(2001)}, 4));
|
||||
assert(mbsn_receive_raw_pdu_response(&CLIENT, raw_res, 2) == MBSN_EXCEPTION_ILLEGAL_DATA_VALUE);
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS from server when calling with address + quantity > 65535");
|
||||
check(mbsn_send_raw_pdu(&CLIENT, 1, (uint16_t[]){htons(65530), htons(7)}, 4));
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS from server when calling with address + quantity > 0xFFFF + 1");
|
||||
check(mbsn_send_raw_pdu(&CLIENT, fc, (uint16_t[]){htons(65530), htons(7)}, 4));
|
||||
assert(mbsn_receive_raw_pdu_response(&CLIENT, raw_res, 2) == MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS);
|
||||
|
||||
should("return MBSN_EXCEPTION_SERVER_DEVICE_FAILURE when server handler returns any non-exception error");
|
||||
@ -206,17 +220,30 @@ void test_fc1(mbsn_transport transport) {
|
||||
assert(mbsn_bitfield_read(bf, 1) == 0);
|
||||
assert(mbsn_bitfield_read(bf, 2) == 1);
|
||||
|
||||
check(mbsn_read_coils(&CLIENT, 65526, 10, bf));
|
||||
assert(mbsn_bitfield_read(bf, 0) == 1);
|
||||
assert(mbsn_bitfield_read(bf, 1) == 0);
|
||||
assert(mbsn_bitfield_read(bf, 2) == 1);
|
||||
assert(mbsn_bitfield_read(bf, 3) == 0);
|
||||
assert(mbsn_bitfield_read(bf, 4) == 1);
|
||||
assert(mbsn_bitfield_read(bf, 5) == 0);
|
||||
assert(mbsn_bitfield_read(bf, 6) == 1);
|
||||
assert(mbsn_bitfield_read(bf, 7) == 0);
|
||||
assert(mbsn_bitfield_read(bf, 8) == 1);
|
||||
assert(mbsn_bitfield_read(bf, 9) == 0);
|
||||
|
||||
stop_client_and_server();
|
||||
}
|
||||
|
||||
|
||||
void test_fc2(mbsn_transport transport) {
|
||||
const uint8_t fc = 2;
|
||||
uint8_t raw_res[260];
|
||||
|
||||
start_client_and_server(transport, (mbsn_callbacks){});
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_FUNCTION when callback is not registered server-side");
|
||||
assert(mbsn_read_discrete_inputs(&CLIENT, 1, 1, NULL) == MBSN_EXCEPTION_ILLEGAL_FUNCTION);
|
||||
assert(mbsn_read_discrete_inputs(&CLIENT, 0, 1, NULL) == MBSN_EXCEPTION_ILLEGAL_FUNCTION);
|
||||
|
||||
stop_client_and_server();
|
||||
|
||||
@ -228,19 +255,19 @@ void test_fc2(mbsn_transport transport) {
|
||||
should("immediately return MBSN_ERROR_INVALID_ARGUMENT when calling with quantity > 2000");
|
||||
assert(mbsn_read_discrete_inputs(&CLIENT, 1, 2001, NULL) == MBSN_ERROR_INVALID_ARGUMENT);
|
||||
|
||||
should("immediately return MBSN_ERROR_INVALID_ARGUMENT when calling with address + quantity > 65535");
|
||||
should("immediately return MBSN_ERROR_INVALID_ARGUMENT when calling with address + quantity > 0xFFFF + 1");
|
||||
assert(mbsn_read_discrete_inputs(&CLIENT, 65530, 7, NULL) == MBSN_ERROR_INVALID_ARGUMENT);
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_DATA_VALUE from server when calling with quantity 0");
|
||||
check(mbsn_send_raw_pdu(&CLIENT, 2, (uint16_t[]){htons(1), htons(0)}, 4));
|
||||
check(mbsn_send_raw_pdu(&CLIENT, fc, (uint16_t[]){htons(1), htons(0)}, 4));
|
||||
assert(mbsn_receive_raw_pdu_response(&CLIENT, raw_res, 2) == MBSN_EXCEPTION_ILLEGAL_DATA_VALUE);
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_DATA_VALUE from server when calling with quantity > 2000");
|
||||
check(mbsn_send_raw_pdu(&CLIENT, 2, (uint16_t[]){htons(1), htons(2001)}, 4));
|
||||
check(mbsn_send_raw_pdu(&CLIENT, fc, (uint16_t[]){htons(1), htons(2001)}, 4));
|
||||
assert(mbsn_receive_raw_pdu_response(&CLIENT, raw_res, 2) == MBSN_EXCEPTION_ILLEGAL_DATA_VALUE);
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS from server when calling with address + quantity > 65535");
|
||||
check(mbsn_send_raw_pdu(&CLIENT, 2, (uint16_t[]){htons(65530), htons(7)}, 4));
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS from server when calling with address + quantity > 0xFFFF + 1");
|
||||
check(mbsn_send_raw_pdu(&CLIENT, fc, (uint16_t[]){htons(65530), htons(7)}, 4));
|
||||
assert(mbsn_receive_raw_pdu_response(&CLIENT, raw_res, 2) == MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS);
|
||||
|
||||
should("return MBSN_EXCEPTION_SERVER_DEVICE_FAILURE when server handler returns any non-exception error");
|
||||
@ -259,13 +286,499 @@ void test_fc2(mbsn_transport transport) {
|
||||
assert(mbsn_bitfield_read(bf, 1) == 0);
|
||||
assert(mbsn_bitfield_read(bf, 2) == 1);
|
||||
|
||||
check(mbsn_read_discrete_inputs(&CLIENT, 65526, 10, bf));
|
||||
assert(mbsn_bitfield_read(bf, 0) == 1);
|
||||
assert(mbsn_bitfield_read(bf, 1) == 0);
|
||||
assert(mbsn_bitfield_read(bf, 2) == 1);
|
||||
assert(mbsn_bitfield_read(bf, 3) == 0);
|
||||
assert(mbsn_bitfield_read(bf, 4) == 1);
|
||||
assert(mbsn_bitfield_read(bf, 5) == 0);
|
||||
assert(mbsn_bitfield_read(bf, 6) == 1);
|
||||
assert(mbsn_bitfield_read(bf, 7) == 0);
|
||||
assert(mbsn_bitfield_read(bf, 8) == 1);
|
||||
assert(mbsn_bitfield_read(bf, 9) == 0);
|
||||
|
||||
stop_client_and_server();
|
||||
}
|
||||
|
||||
|
||||
mbsn_transport transports[2] = {MBSN_TRANSPORT_TCP, MBSN_TRANSPORT_RTU};
|
||||
const char* transports_str[2] = {"TCP", "RTU"};
|
||||
mbsn_error read_registers(uint16_t address, uint16_t quantity, uint16_t* registers_out) {
|
||||
if (address == 1)
|
||||
return -1;
|
||||
|
||||
if (address == 2)
|
||||
return MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS;
|
||||
|
||||
if (address == 3)
|
||||
return MBSN_EXCEPTION_ILLEGAL_DATA_VALUE;
|
||||
|
||||
if (address == 10 && quantity == 3) {
|
||||
registers_out[0] = 100;
|
||||
registers_out[1] = 0;
|
||||
registers_out[2] = 200;
|
||||
}
|
||||
|
||||
return MBSN_ERROR_NONE;
|
||||
}
|
||||
|
||||
|
||||
void test_fc3(mbsn_transport transport) {
|
||||
const uint8_t fc = 3;
|
||||
uint8_t raw_res[260];
|
||||
|
||||
start_client_and_server(transport, (mbsn_callbacks){});
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_FUNCTION when callback is not registered server-side");
|
||||
assert(mbsn_read_holding_registers(&CLIENT, 0, 1, NULL) == MBSN_EXCEPTION_ILLEGAL_FUNCTION);
|
||||
|
||||
stop_client_and_server();
|
||||
|
||||
start_client_and_server(transport, (mbsn_callbacks){.read_holding_registers = read_registers});
|
||||
|
||||
should("immediately return MBSN_ERROR_INVALID_ARGUMENT when calling with quantity 0");
|
||||
assert(mbsn_read_holding_registers(&CLIENT, 1, 0, NULL) == MBSN_ERROR_INVALID_ARGUMENT);
|
||||
|
||||
should("immediately return MBSN_ERROR_INVALID_ARGUMENT when calling with quantity > 125");
|
||||
assert(mbsn_read_holding_registers(&CLIENT, 1, 126, NULL) == MBSN_ERROR_INVALID_ARGUMENT);
|
||||
|
||||
should("immediately return MBSN_ERROR_INVALID_ARGUMENT when calling with address + quantity > 0xFFFF + 1");
|
||||
assert(mbsn_read_holding_registers(&CLIENT, 0xFFFF, 2, NULL) == MBSN_ERROR_INVALID_ARGUMENT);
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_DATA_VALUE from server when calling with quantity 0");
|
||||
check(mbsn_send_raw_pdu(&CLIENT, fc, (uint16_t[]){htons(1), htons(0)}, 4));
|
||||
assert(mbsn_receive_raw_pdu_response(&CLIENT, raw_res, 2) == MBSN_EXCEPTION_ILLEGAL_DATA_VALUE);
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_DATA_VALUE from server when calling with quantity > 2000");
|
||||
check(mbsn_send_raw_pdu(&CLIENT, fc, (uint16_t[]){htons(1), htons(2001)}, 4));
|
||||
assert(mbsn_receive_raw_pdu_response(&CLIENT, raw_res, 2) == MBSN_EXCEPTION_ILLEGAL_DATA_VALUE);
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS from server when calling with address + quantity > 0xFFFF + 1");
|
||||
check(mbsn_send_raw_pdu(&CLIENT, fc, (uint16_t[]){htons(0xFFFF), htons(2)}, 4));
|
||||
assert(mbsn_receive_raw_pdu_response(&CLIENT, raw_res, 2) == MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS);
|
||||
|
||||
should("return MBSN_EXCEPTION_SERVER_DEVICE_FAILURE when server handler returns any non-exception error");
|
||||
assert(mbsn_read_holding_registers(&CLIENT, 1, 1, NULL) == MBSN_EXCEPTION_SERVER_DEVICE_FAILURE);
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS if returned by server handler");
|
||||
assert(mbsn_read_holding_registers(&CLIENT, 2, 1, NULL) == MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS);
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_DATA_VALUE if returned by server handler");
|
||||
assert(mbsn_read_holding_registers(&CLIENT, 3, 1, NULL) == MBSN_EXCEPTION_ILLEGAL_DATA_VALUE);
|
||||
|
||||
should("read with no error");
|
||||
uint16_t regs[3];
|
||||
check(mbsn_read_holding_registers(&CLIENT, 10, 3, regs));
|
||||
assert(regs[0] == 100);
|
||||
assert(regs[1] == 0);
|
||||
assert(regs[2] == 200);
|
||||
|
||||
stop_client_and_server();
|
||||
}
|
||||
|
||||
|
||||
void test_fc4(mbsn_transport transport) {
|
||||
const uint8_t fc = 4;
|
||||
uint8_t raw_res[260];
|
||||
|
||||
start_client_and_server(transport, (mbsn_callbacks){});
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_FUNCTION when callback is not registered server-side");
|
||||
assert(mbsn_read_input_registers(&CLIENT, 0, 1, NULL) == MBSN_EXCEPTION_ILLEGAL_FUNCTION);
|
||||
|
||||
stop_client_and_server();
|
||||
|
||||
start_client_and_server(transport, (mbsn_callbacks){.read_input_registers = read_registers});
|
||||
|
||||
should("immediately return MBSN_ERROR_INVALID_ARGUMENT when calling with quantity 0");
|
||||
assert(mbsn_read_input_registers(&CLIENT, 1, 0, NULL) == MBSN_ERROR_INVALID_ARGUMENT);
|
||||
|
||||
should("immediately return MBSN_ERROR_INVALID_ARGUMENT when calling with quantity > 125");
|
||||
assert(mbsn_read_input_registers(&CLIENT, 1, 126, NULL) == MBSN_ERROR_INVALID_ARGUMENT);
|
||||
|
||||
should("immediately return MBSN_ERROR_INVALID_ARGUMENT when calling with address + quantity > 0xFFFF + 1");
|
||||
assert(mbsn_read_input_registers(&CLIENT, 0xFFFF, 2, NULL) == MBSN_ERROR_INVALID_ARGUMENT);
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_DATA_VALUE from server when calling with quantity 0");
|
||||
check(mbsn_send_raw_pdu(&CLIENT, fc, (uint16_t[]){htons(1), htons(0)}, 4));
|
||||
assert(mbsn_receive_raw_pdu_response(&CLIENT, raw_res, 2) == MBSN_EXCEPTION_ILLEGAL_DATA_VALUE);
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_DATA_VALUE from server when calling with quantity > 2000");
|
||||
check(mbsn_send_raw_pdu(&CLIENT, fc, (uint16_t[]){htons(1), htons(2001)}, 4));
|
||||
assert(mbsn_receive_raw_pdu_response(&CLIENT, raw_res, 2) == MBSN_EXCEPTION_ILLEGAL_DATA_VALUE);
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS from server when calling with address + quantity > 0xFFFF + 1");
|
||||
check(mbsn_send_raw_pdu(&CLIENT, fc, (uint16_t[]){htons(0xFFFF), htons(2)}, 4));
|
||||
assert(mbsn_receive_raw_pdu_response(&CLIENT, raw_res, 2) == MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS);
|
||||
|
||||
should("return MBSN_EXCEPTION_SERVER_DEVICE_FAILURE when server handler returns any non-exception error");
|
||||
assert(mbsn_read_input_registers(&CLIENT, 1, 1, NULL) == MBSN_EXCEPTION_SERVER_DEVICE_FAILURE);
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS if returned by server handler");
|
||||
assert(mbsn_read_input_registers(&CLIENT, 2, 1, NULL) == MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS);
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_DATA_VALUE if returned by server handler");
|
||||
assert(mbsn_read_input_registers(&CLIENT, 3, 1, NULL) == MBSN_EXCEPTION_ILLEGAL_DATA_VALUE);
|
||||
|
||||
should("read with no error");
|
||||
uint16_t regs[3];
|
||||
check(mbsn_read_input_registers(&CLIENT, 10, 3, regs));
|
||||
assert(regs[0] == 100);
|
||||
assert(regs[1] == 0);
|
||||
assert(regs[2] == 200);
|
||||
|
||||
stop_client_and_server();
|
||||
}
|
||||
|
||||
|
||||
mbsn_error write_coil(uint16_t address, bool value) {
|
||||
if (address == 1)
|
||||
return -1;
|
||||
|
||||
if (address == 2)
|
||||
return MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS;
|
||||
|
||||
if (address == 3)
|
||||
return MBSN_EXCEPTION_ILLEGAL_DATA_VALUE;
|
||||
|
||||
if (address == 4 && !value)
|
||||
return MBSN_EXCEPTION_SERVER_DEVICE_FAILURE;
|
||||
|
||||
if (address == 5 && value)
|
||||
return MBSN_EXCEPTION_SERVER_DEVICE_FAILURE;
|
||||
|
||||
return MBSN_ERROR_NONE;
|
||||
}
|
||||
|
||||
|
||||
void test_fc5(mbsn_transport transport) {
|
||||
const uint8_t fc = 5;
|
||||
uint8_t raw_res[260];
|
||||
|
||||
start_client_and_server(transport, (mbsn_callbacks){});
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_FUNCTION when callback is not registered server-side");
|
||||
assert(mbsn_write_single_coil(&CLIENT, 0, true) == MBSN_EXCEPTION_ILLEGAL_FUNCTION);
|
||||
|
||||
stop_client_and_server();
|
||||
|
||||
start_client_and_server(transport, (mbsn_callbacks){.write_single_coil = write_coil});
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_DATA_VALUE when calling with value !0x0000 or 0xFF000");
|
||||
check(mbsn_send_raw_pdu(&CLIENT, fc, (uint16_t[]){htons(6), htons(0x0001)}, 4));
|
||||
assert(mbsn_receive_raw_pdu_response(&CLIENT, raw_res, 2) == MBSN_EXCEPTION_ILLEGAL_DATA_VALUE);
|
||||
|
||||
check(mbsn_send_raw_pdu(&CLIENT, fc, (uint16_t[]){htons(6), htons(0xFFFF)}, 4));
|
||||
assert(mbsn_receive_raw_pdu_response(&CLIENT, raw_res, 2) == MBSN_EXCEPTION_ILLEGAL_DATA_VALUE);
|
||||
|
||||
should("return MBSN_EXCEPTION_SERVER_DEVICE_FAILURE when server handler returns any non-exception error");
|
||||
assert(mbsn_write_single_coil(&CLIENT, 1, true) == MBSN_EXCEPTION_SERVER_DEVICE_FAILURE);
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS if returned by server handler");
|
||||
assert(mbsn_write_single_coil(&CLIENT, 2, true) == MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS);
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_DATA_VALUE if returned by server handler");
|
||||
assert(mbsn_write_single_coil(&CLIENT, 3, true) == MBSN_EXCEPTION_ILLEGAL_DATA_VALUE);
|
||||
|
||||
should("write with no error");
|
||||
check(mbsn_write_single_coil(&CLIENT, 4, true));
|
||||
check(mbsn_write_single_coil(&CLIENT, 5, false));
|
||||
|
||||
should("echo request's address and value");
|
||||
check(mbsn_send_raw_pdu(&CLIENT, fc, (uint16_t[]){htons(4), htons(0xFF00)}, 4));
|
||||
check(mbsn_receive_raw_pdu_response(&CLIENT, raw_res, 4));
|
||||
|
||||
assert(((uint16_t*) raw_res)[0] == ntohs(4));
|
||||
assert(((uint16_t*) raw_res)[1] == ntohs(0xFF00));
|
||||
|
||||
stop_client_and_server();
|
||||
}
|
||||
|
||||
|
||||
mbsn_error write_register(uint16_t address, uint16_t value) {
|
||||
if (address == 1)
|
||||
return -1;
|
||||
|
||||
if (address == 2)
|
||||
return MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS;
|
||||
|
||||
if (address == 3)
|
||||
return MBSN_EXCEPTION_ILLEGAL_DATA_VALUE;
|
||||
|
||||
if (address == 4 && !value)
|
||||
return MBSN_EXCEPTION_SERVER_DEVICE_FAILURE;
|
||||
|
||||
if (address == 5 && value)
|
||||
return MBSN_EXCEPTION_SERVER_DEVICE_FAILURE;
|
||||
|
||||
return MBSN_ERROR_NONE;
|
||||
}
|
||||
|
||||
|
||||
void test_fc6(mbsn_transport transport) {
|
||||
const uint8_t fc = 6;
|
||||
uint8_t raw_res[260];
|
||||
|
||||
start_client_and_server(transport, (mbsn_callbacks){});
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_FUNCTION when callback is not registered server-side");
|
||||
assert(mbsn_write_single_register(&CLIENT, 0, 123) == MBSN_EXCEPTION_ILLEGAL_FUNCTION);
|
||||
|
||||
stop_client_and_server();
|
||||
|
||||
start_client_and_server(transport, (mbsn_callbacks){.write_single_register = write_register});
|
||||
|
||||
should("return MBSN_EXCEPTION_SERVER_DEVICE_FAILURE when server handler returns any non-exception error");
|
||||
assert(mbsn_write_single_register(&CLIENT, 1, 123) == MBSN_EXCEPTION_SERVER_DEVICE_FAILURE);
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS if returned by server handler");
|
||||
assert(mbsn_write_single_register(&CLIENT, 2, 123) == MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS);
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_DATA_VALUE if returned by server handler");
|
||||
assert(mbsn_write_single_register(&CLIENT, 3, 123) == MBSN_EXCEPTION_ILLEGAL_DATA_VALUE);
|
||||
|
||||
should("write with no error");
|
||||
check(mbsn_write_single_register(&CLIENT, 4, true));
|
||||
check(mbsn_write_single_register(&CLIENT, 5, false));
|
||||
|
||||
should("echo request's address and value");
|
||||
check(mbsn_send_raw_pdu(&CLIENT, fc, (uint16_t[]){htons(4), htons(0x123)}, 4));
|
||||
check(mbsn_receive_raw_pdu_response(&CLIENT, raw_res, 4));
|
||||
|
||||
assert(((uint16_t*) raw_res)[0] == ntohs(4));
|
||||
assert(((uint16_t*) raw_res)[1] == ntohs(0x123));
|
||||
|
||||
stop_client_and_server();
|
||||
}
|
||||
|
||||
|
||||
mbsn_error write_coils(uint16_t address, uint16_t quantity, const mbsn_bitfield coils) {
|
||||
if (address == 1)
|
||||
return -1;
|
||||
|
||||
if (address == 2)
|
||||
return MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS;
|
||||
|
||||
if (address == 3)
|
||||
return MBSN_EXCEPTION_ILLEGAL_DATA_VALUE;
|
||||
|
||||
if (address == 4) {
|
||||
if (quantity != 4)
|
||||
return MBSN_EXCEPTION_SERVER_DEVICE_FAILURE;
|
||||
|
||||
assert(mbsn_bitfield_read(coils, 0) == 1);
|
||||
assert(mbsn_bitfield_read(coils, 1) == 0);
|
||||
assert(mbsn_bitfield_read(coils, 2) == 1);
|
||||
assert(mbsn_bitfield_read(coils, 3) == 0);
|
||||
|
||||
return MBSN_ERROR_NONE;
|
||||
}
|
||||
|
||||
if (address == 5) {
|
||||
if (quantity != 27)
|
||||
return MBSN_EXCEPTION_SERVER_DEVICE_FAILURE;
|
||||
|
||||
assert(mbsn_bitfield_read(coils, 26) == 1);
|
||||
|
||||
return MBSN_ERROR_NONE;
|
||||
}
|
||||
|
||||
if (address == 7) {
|
||||
if (quantity != 1)
|
||||
return MBSN_EXCEPTION_SERVER_DEVICE_FAILURE;
|
||||
|
||||
return MBSN_ERROR_NONE;
|
||||
}
|
||||
|
||||
return MBSN_ERROR_NONE;
|
||||
}
|
||||
|
||||
|
||||
void test_fc15(mbsn_transport transport) {
|
||||
const uint8_t fc = 15;
|
||||
uint8_t raw_res[260];
|
||||
mbsn_bitfield bf = {0};
|
||||
|
||||
start_client_and_server(transport, (mbsn_callbacks){});
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_FUNCTION when callback is not registered server-side");
|
||||
assert(mbsn_write_multiple_coils(&CLIENT, 0, 1, bf) == MBSN_EXCEPTION_ILLEGAL_FUNCTION);
|
||||
|
||||
stop_client_and_server();
|
||||
|
||||
start_client_and_server(transport, (mbsn_callbacks){.write_multiple_coils = write_coils});
|
||||
|
||||
should("immediately return MBSN_ERROR_INVALID_ARGUMENT when calling with quantity 0");
|
||||
assert(mbsn_write_multiple_coils(&CLIENT, 1, 0, bf) == MBSN_ERROR_INVALID_ARGUMENT);
|
||||
|
||||
should("immediately return MBSN_ERROR_INVALID_ARGUMENT when calling with quantity > 0x07B0");
|
||||
assert(mbsn_write_multiple_coils(&CLIENT, 1, 0x07B1, bf) == MBSN_ERROR_INVALID_ARGUMENT);
|
||||
|
||||
should("immediately return MBSN_ERROR_INVALID_ARGUMENT when calling with address + quantity > 0xFFFF + 1");
|
||||
assert(mbsn_write_multiple_coils(&CLIENT, 0xFFFF, 2, bf) == MBSN_ERROR_INVALID_ARGUMENT);
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_DATA_VALUE from server when calling with quantity 0");
|
||||
check(mbsn_send_raw_pdu(&CLIENT, fc, (uint16_t[]){htons(1), htons(0), htons(0x0100)}, 6));
|
||||
assert(mbsn_receive_raw_pdu_response(&CLIENT, raw_res, 2) == MBSN_EXCEPTION_ILLEGAL_DATA_VALUE);
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_DATA_VALUE from server when calling with quantity > 2000");
|
||||
check(mbsn_send_raw_pdu(&CLIENT, fc, (uint16_t[]){htons(1), htons(2000), htons(0x0100)}, 6));
|
||||
assert(mbsn_receive_raw_pdu_response(&CLIENT, raw_res, 2) == MBSN_EXCEPTION_ILLEGAL_DATA_VALUE);
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS from server when calling with address + quantity > 0xFFFF + 1");
|
||||
check(mbsn_send_raw_pdu(&CLIENT, fc, (uint16_t[]){htons(0xFFFF), htons(2), htons(0x0100)}, 6));
|
||||
assert(mbsn_receive_raw_pdu_response(&CLIENT, raw_res, 2) == MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS);
|
||||
|
||||
/*
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS from server when quantity does not match byte count");
|
||||
check(mbsn_send_raw_pdu(&CLIENT, fc, (uint16_t[]){htons(1), htons(5), htons(0x0303)}, 6));
|
||||
assert(mbsn_receive_raw_pdu_response(&CLIENT, raw_res, 2) == MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS);
|
||||
*/
|
||||
|
||||
should("return MBSN_EXCEPTION_SERVER_DEVICE_FAILURE when server handler returns any non-exception error");
|
||||
assert(mbsn_write_multiple_coils(&CLIENT, 1, 1, bf) == MBSN_EXCEPTION_SERVER_DEVICE_FAILURE);
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS if returned by server handler");
|
||||
assert(mbsn_write_multiple_coils(&CLIENT, 2, 2, bf) == MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS);
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_DATA_VALUE if returned by server handler");
|
||||
assert(mbsn_write_multiple_coils(&CLIENT, 3, 3, bf) == MBSN_EXCEPTION_ILLEGAL_DATA_VALUE);
|
||||
|
||||
should("write with no error");
|
||||
mbsn_bitfield_write(bf, 0, 1);
|
||||
mbsn_bitfield_write(bf, 1, 0);
|
||||
mbsn_bitfield_write(bf, 2, 1);
|
||||
mbsn_bitfield_write(bf, 3, 0);
|
||||
check(mbsn_write_multiple_coils(&CLIENT, 4, 4, bf));
|
||||
|
||||
mbsn_bitfield_write(bf, 26, 1);
|
||||
check(mbsn_write_multiple_coils(&CLIENT, 5, 27, bf));
|
||||
|
||||
should("echo request's address and value");
|
||||
check(mbsn_send_raw_pdu(&CLIENT, fc, (uint16_t[]){htons(7), htons(1), htons(0x0100)}, 6));
|
||||
check(mbsn_receive_raw_pdu_response(&CLIENT, raw_res, 4));
|
||||
|
||||
assert(((uint16_t*) raw_res)[0] == ntohs(7));
|
||||
assert(((uint16_t*) raw_res)[1] == ntohs(1));
|
||||
|
||||
stop_client_and_server();
|
||||
}
|
||||
|
||||
|
||||
mbsn_error write_registers(uint16_t address, uint16_t quantity, const uint16_t* registers) {
|
||||
if (address == 1)
|
||||
return -1;
|
||||
|
||||
if (address == 2)
|
||||
return MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS;
|
||||
|
||||
if (address == 3)
|
||||
return MBSN_EXCEPTION_ILLEGAL_DATA_VALUE;
|
||||
|
||||
if (address == 4) {
|
||||
if (quantity != 4)
|
||||
return MBSN_EXCEPTION_SERVER_DEVICE_FAILURE;
|
||||
|
||||
assert(registers[0] == 255);
|
||||
assert(registers[1] == 1);
|
||||
assert(registers[2] == 2);
|
||||
assert(registers[3] == 3);
|
||||
|
||||
return MBSN_ERROR_NONE;
|
||||
}
|
||||
|
||||
if (address == 5) {
|
||||
if (quantity != 27)
|
||||
return MBSN_EXCEPTION_SERVER_DEVICE_FAILURE;
|
||||
|
||||
assert(registers[26] == 26);
|
||||
|
||||
return MBSN_ERROR_NONE;
|
||||
}
|
||||
|
||||
if (address == 7) {
|
||||
if (quantity != 1)
|
||||
return MBSN_EXCEPTION_SERVER_DEVICE_FAILURE;
|
||||
|
||||
return MBSN_ERROR_NONE;
|
||||
}
|
||||
|
||||
return MBSN_ERROR_NONE;
|
||||
}
|
||||
|
||||
|
||||
void test_fc16(mbsn_transport transport) {
|
||||
const uint8_t fc = 16;
|
||||
uint8_t raw_res[260];
|
||||
uint16_t registers[125];
|
||||
|
||||
start_client_and_server(transport, (mbsn_callbacks){});
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_FUNCTION when callback is not registered server-side");
|
||||
assert(mbsn_write_multiple_registers(&CLIENT, 0, 1, registers) == MBSN_EXCEPTION_ILLEGAL_FUNCTION);
|
||||
|
||||
stop_client_and_server();
|
||||
|
||||
start_client_and_server(transport, (mbsn_callbacks){.write_multiple_registers = write_registers});
|
||||
|
||||
should("immediately return MBSN_ERROR_INVALID_ARGUMENT when calling with quantity 0");
|
||||
assert(mbsn_write_multiple_registers(&CLIENT, 1, 0, registers) == MBSN_ERROR_INVALID_ARGUMENT);
|
||||
|
||||
should("immediately return MBSN_ERROR_INVALID_ARGUMENT when calling with quantity > 0x007B");
|
||||
assert(mbsn_write_multiple_registers(&CLIENT, 1, 0x007C, registers) == MBSN_ERROR_INVALID_ARGUMENT);
|
||||
|
||||
should("immediately return MBSN_ERROR_INVALID_ARGUMENT when calling with address + quantity > 0xFFFF + 1");
|
||||
assert(mbsn_write_multiple_registers(&CLIENT, 0xFFFF, 2, registers) == MBSN_ERROR_INVALID_ARGUMENT);
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_DATA_VALUE from server when calling with quantity 0");
|
||||
check(mbsn_send_raw_pdu(&CLIENT, fc, (uint16_t[]){htons(1), htons(0), htons(0x0200), htons(0)}, 7));
|
||||
assert(mbsn_receive_raw_pdu_response(&CLIENT, raw_res, 2) == MBSN_EXCEPTION_ILLEGAL_DATA_VALUE);
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_DATA_VALUE from server when calling with quantity > 2000");
|
||||
check(mbsn_send_raw_pdu(&CLIENT, fc, (uint16_t[]){htons(1), htons(2000), htons(0x0200), htons(0)}, 7));
|
||||
assert(mbsn_receive_raw_pdu_response(&CLIENT, raw_res, 2) == MBSN_EXCEPTION_ILLEGAL_DATA_VALUE);
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS from server when calling with address + quantity > 0xFFFF + 1");
|
||||
check(mbsn_send_raw_pdu(&CLIENT, fc, (uint16_t[]){htons(0xFFFF), htons(2), htons(0x0200), htons(0)}, 7));
|
||||
assert(mbsn_receive_raw_pdu_response(&CLIENT, raw_res, 2) == MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS);
|
||||
|
||||
/*
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS from server when quantity does not match byte count");
|
||||
check(mbsn_send_raw_pdu(&CLIENT, fc, (uint16_t[]){htons(1), htons(5), htons(0x0303)}, 6));
|
||||
assert(mbsn_receive_raw_pdu_response(&CLIENT, raw_res, 2) == MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS);
|
||||
*/
|
||||
|
||||
should("return MBSN_EXCEPTION_SERVER_DEVICE_FAILURE when server handler returns any non-exception error");
|
||||
assert(mbsn_write_multiple_registers(&CLIENT, 1, 1, registers) == MBSN_EXCEPTION_SERVER_DEVICE_FAILURE);
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS if returned by server handler");
|
||||
assert(mbsn_write_multiple_registers(&CLIENT, 2, 2, registers) == MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS);
|
||||
|
||||
should("return MBSN_EXCEPTION_ILLEGAL_DATA_VALUE if returned by server handler");
|
||||
assert(mbsn_write_multiple_registers(&CLIENT, 3, 3, registers) == MBSN_EXCEPTION_ILLEGAL_DATA_VALUE);
|
||||
|
||||
should("write with no error");
|
||||
registers[0] = 255;
|
||||
registers[1] = 1;
|
||||
registers[2] = 2;
|
||||
registers[3] = 3;
|
||||
check(mbsn_write_multiple_registers(&CLIENT, 4, 4, registers));
|
||||
|
||||
registers[26] = 26;
|
||||
check(mbsn_write_multiple_registers(&CLIENT, 6, 27, registers));
|
||||
|
||||
should("echo request's address and value");
|
||||
check(mbsn_send_raw_pdu(&CLIENT, fc, (uint16_t[]){htons(7), htons(1), htons(0x0200), htons(0)}, 7));
|
||||
check(mbsn_receive_raw_pdu_response(&CLIENT, raw_res, 4));
|
||||
|
||||
assert(((uint16_t*) raw_res)[0] == ntohs(7));
|
||||
assert(((uint16_t*) raw_res)[1] == ntohs(1));
|
||||
|
||||
stop_client_and_server();
|
||||
}
|
||||
|
||||
|
||||
mbsn_transport transports[2] = {MBSN_TRANSPORT_RTU, MBSN_TRANSPORT_TCP};
|
||||
const char* transports_str[2] = {"RTU", "TCP"};
|
||||
|
||||
void for_transports(void (*test_fn)(mbsn_transport), const char* should_str) {
|
||||
for (int t = 0; t < sizeof(transports) / sizeof(mbsn_transport); t++) {
|
||||
@ -274,9 +787,7 @@ void for_transports(void (*test_fn)(mbsn_transport), const char* should_str) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
|
||||
for_transports(test_server_create, "create a modbus server");
|
||||
|
||||
for_transports(test_server_receive_base, "receive no messages without failing");
|
||||
@ -285,6 +796,17 @@ int main() {
|
||||
|
||||
for_transports(test_fc2, "send and receive FC 02 (0x02) Read Discrete Inputs");
|
||||
|
||||
for_transports(test_fc3, "send and receive FC 03 (0x03) Read Holding Registers");
|
||||
|
||||
for_transports(test_fc4, "send and receive FC 04 (0x04) Read Input Registers");
|
||||
|
||||
for_transports(test_fc5, "send and receive FC 05 (0x05) Write Single Coil");
|
||||
|
||||
for_transports(test_fc6, "send and receive FC 06 (0x06) Write Single Register");
|
||||
|
||||
for_transports(test_fc15, "send and receive FC 15 (0x0F) Write Multiple Coils");
|
||||
|
||||
for_transports(test_fc16, "send and receive FC 16 (0x10) Write Multiple registers");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -209,10 +209,10 @@ void start_client_and_server(mbsn_transport transport, mbsn_callbacks server_cal
|
||||
check(mbsn_client_create(&CLIENT, platform_conf_pipe_client(transport)));
|
||||
|
||||
mbsn_set_destination_rtu_address(&CLIENT, TEST_SERVER_ADDR);
|
||||
mbsn_set_read_timeout(&SERVER, 1000);
|
||||
mbsn_set_read_timeout(&SERVER, 500);
|
||||
mbsn_set_byte_timeout(&SERVER, 100);
|
||||
|
||||
mbsn_set_read_timeout(&CLIENT, -1);
|
||||
mbsn_set_read_timeout(&CLIENT, 5000);
|
||||
mbsn_set_byte_timeout(&CLIENT, 100);
|
||||
|
||||
assert(pthread_mutex_lock(&server_stopped_m) == 0);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user