Additional tests

This commit is contained in:
Valerio De Benedetto 2022-01-22 11:34:47 +01:00
parent c064e47368
commit 13cd7c74b6
5 changed files with 578 additions and 55 deletions

View File

@ -1,7 +1,7 @@
#include "modbusino.h" #include "modbusino.h"
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h>
#ifdef MBSN_DEBUG #ifdef MBSN_DEBUG
#include <stdio.h> #include <stdio.h>
@ -43,18 +43,6 @@
(m)->msg.buf_idx += 2 (m)->msg.buf_idx += 2
#endif #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) { static void msg_buf_reset(mbsn_t* mbsn) {
mbsn->msg.buf_idx = 0; 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->byte_timeout_ms = -1;
mbsn->read_timeout_ms = -1; mbsn->read_timeout_ms = -1;
mbsn->byte_spacing_ms = 0;
if (!platform_conf) if (!platform_conf)
return MBSN_ERROR_INVALID_ARGUMENT; 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; mbsn->platform = *platform_conf;
return MBSN_ERROR_NONE; 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) { void mbsn_set_destination_rtu_address(mbsn_t* mbsn, uint8_t address) {
mbsn->dest_address_rtu = 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) { 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++) { 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); int ret = mbsn->platform.write_byte(mbsn->msg.buf[i], mbsn->read_timeout_ms);
if (ret == 0) { if (ret == 0) {
return MBSN_ERROR_TIMEOUT; return MBSN_ERROR_TIMEOUT;
@ -539,10 +539,10 @@ static mbsn_error handle_write_single_coil(mbsn_t* mbsn) {
return err; return err;
if (!mbsn->msg.ignored) { if (!mbsn->msg.ignored) {
if (value != 0 && value != 0xFF00)
return handle_exception(mbsn, MBSN_EXCEPTION_ILLEGAL_DATA_VALUE);
if (mbsn->callbacks.write_single_coil) { if (mbsn->callbacks.write_single_coil) {
if (value != 0 && value != 0xFF00)
return handle_exception(mbsn, MBSN_EXCEPTION_ILLEGAL_DATA_VALUE);
err = mbsn->callbacks.write_single_coil(address, value == 0 ? false : true); err = mbsn->callbacks.write_single_coil(address, value == 0 ? false : true);
if (err != MBSN_ERROR_NONE) { if (err != MBSN_ERROR_NONE) {
if (mbsn_error_is_exception(err)) if (mbsn_error_is_exception(err))
@ -552,8 +552,9 @@ static mbsn_error handle_write_single_coil(mbsn_t* mbsn) {
} }
if (!mbsn->msg.broadcast) { if (!mbsn->msg.broadcast) {
send_msg_header(mbsn, 2); send_msg_header(mbsn, 4);
put_2(mbsn, address); put_2(mbsn, address);
put_2(mbsn, value);
err = send_msg_footer(mbsn); err = send_msg_footer(mbsn);
if (err != MBSN_ERROR_NONE) if (err != MBSN_ERROR_NONE)
return err; return err;
@ -591,7 +592,8 @@ static mbsn_error handle_write_single_register(mbsn_t* mbsn) {
} }
if (!mbsn->msg.broadcast) { if (!mbsn->msg.broadcast) {
send_msg_header(mbsn, 1); send_msg_header(mbsn, 4);
put_2(mbsn, address);
put_2(mbsn, value); put_2(mbsn, value);
err = send_msg_footer(mbsn); err = send_msg_footer(mbsn);
if (err != MBSN_ERROR_NONE) if (err != MBSN_ERROR_NONE)
@ -621,8 +623,9 @@ static mbsn_error handle_write_multiple_coils(mbsn_t* mbsn) {
return err; return err;
mbsn_bitfield coils; mbsn_bitfield coils;
for (int i = 0; i < coils_bytes; i++) for (int i = 0; i < coils_bytes; i++) {
coils[i] = get_1(mbsn); coils[i] = get_1(mbsn);
}
err = recv_msg_footer(mbsn); err = recv_msg_footer(mbsn);
if (err != MBSN_ERROR_NONE) if (err != MBSN_ERROR_NONE)
@ -682,7 +685,7 @@ static mbsn_error handle_write_multiple_registers(mbsn_t* mbsn) {
return err; return err;
uint16_t registers[0x007B]; 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); 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) { 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) if (quantity < 1 || quantity > 2000)
return MBSN_ERROR_INVALID_ARGUMENT; 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) { static 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;
if (quantity < 1 || quantity > 125) if (quantity < 1 || quantity > 125)
return MBSN_ERROR_INVALID_ARGUMENT; 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); msg_state_req(mbsn, 5);
send_msg_header(mbsn, 4); send_msg_header(mbsn, 4);
uint16_t value_req = value ? 0xFF00 : 0;
put_2(mbsn, address); put_2(mbsn, address);
put_2(mbsn, value ? 0xFF00 : 0); put_2(mbsn, value_req);
mbsn_error err = send_msg_footer(mbsn); mbsn_error err = send_msg_footer(mbsn);
if (err != MBSN_ERROR_NONE) 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) if (address_res != address)
return MBSN_ERROR_INVALID_RESPONSE; return MBSN_ERROR_INVALID_RESPONSE;
if (value_res != value) if (value_res != value_req)
return MBSN_ERROR_INVALID_RESPONSE; 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) { 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); send_msg_header(mbsn, 4);
put_2(mbsn, address); 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) { 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; return MBSN_ERROR_INVALID_ARGUMENT;
if ((uint32_t) address + (uint32_t) quantity > 0xFFFF + 1) 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) { 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; return MBSN_ERROR_INVALID_ARGUMENT;
if ((uint32_t) address + (uint32_t) quantity > 0xFFFF + 1) if ((uint32_t) address + (uint32_t) quantity > 0xFFFF + 1)

View File

@ -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 (*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_coil)(uint16_t address, bool value);
mbsn_error (*write_single_register)(uint16_t address, uint16_t 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_coils)(uint16_t address, uint16_t quantity, const mbsn_bitfield coils);
mbsn_error (*write_multiple_registers)(uint16_t address, uint16_t quantity, uint16_t* registers); mbsn_error (*write_multiple_registers)(uint16_t address, uint16_t quantity, const uint16_t* registers);
} mbsn_callbacks; } mbsn_callbacks;
@ -72,6 +72,7 @@ typedef struct mbsn_t {
int32_t byte_timeout_ms; int32_t byte_timeout_ms;
int32_t read_timeout_ms; int32_t read_timeout_ms;
uint32_t byte_spacing_ms;
mbsn_platform_conf platform; mbsn_platform_conf platform;

View File

@ -2,11 +2,12 @@ cmake_minimum_required(VERSION 3.21)
project(modbusino_tests C) project(modbusino_tests C)
set(CMAKE_C_STANDARD 99) 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(. ..) include_directories(. ..)
add_definitions(-DMBSN_DEBUG) #add_definitions(-DMBSN_DEBUG)
add_executable(modbusino_tests ../modbusino.c modbusino_tests.c) add_executable(modbusino_tests ../modbusino.c modbusino_tests.c)

View File

@ -153,17 +153,31 @@ mbsn_error read_discrete(uint16_t address, uint16_t quantity, mbsn_bitfield coil
mbsn_bitfield_write(coils_out, 2, 1); 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; return MBSN_ERROR_NONE;
} }
void test_fc1(mbsn_transport transport) { void test_fc1(mbsn_transport transport) {
const uint8_t fc = 1;
uint8_t raw_res[260]; uint8_t raw_res[260];
start_client_and_server(transport, (mbsn_callbacks){}); start_client_and_server(transport, (mbsn_callbacks){});
should("return MBSN_EXCEPTION_ILLEGAL_FUNCTION when callback is not registered server-side"); 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(); 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"); should("immediately return MBSN_ERROR_INVALID_ARGUMENT when calling with quantity > 2000");
assert(mbsn_read_coils(&CLIENT, 1, 2001, NULL) == MBSN_ERROR_INVALID_ARGUMENT); 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); 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"); 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); 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"); 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); 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"); should("return MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS from server when calling with address + quantity > 0xFFFF + 1");
check(mbsn_send_raw_pdu(&CLIENT, 1, (uint16_t[]){htons(65530), htons(7)}, 4)); 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); 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"); 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, 1) == 0);
assert(mbsn_bitfield_read(bf, 2) == 1); 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(); stop_client_and_server();
} }
void test_fc2(mbsn_transport transport) { void test_fc2(mbsn_transport transport) {
const uint8_t fc = 2;
uint8_t raw_res[260]; uint8_t raw_res[260];
start_client_and_server(transport, (mbsn_callbacks){}); start_client_and_server(transport, (mbsn_callbacks){});
should("return MBSN_EXCEPTION_ILLEGAL_FUNCTION when callback is not registered server-side"); 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(); 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"); 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); 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); 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"); 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); 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"); 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); 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"); should("return MBSN_EXCEPTION_ILLEGAL_DATA_ADDRESS from server when calling with address + quantity > 0xFFFF + 1");
check(mbsn_send_raw_pdu(&CLIENT, 2, (uint16_t[]){htons(65530), htons(7)}, 4)); 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); 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"); 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, 1) == 0);
assert(mbsn_bitfield_read(bf, 2) == 1); 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(); stop_client_and_server();
} }
mbsn_transport transports[2] = {MBSN_TRANSPORT_TCP, MBSN_TRANSPORT_RTU}; mbsn_error read_registers(uint16_t address, uint16_t quantity, uint16_t* registers_out) {
const char* transports_str[2] = {"TCP", "RTU"}; 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) { void for_transports(void (*test_fn)(mbsn_transport), const char* should_str) {
for (int t = 0; t < sizeof(transports) / sizeof(mbsn_transport); t++) { 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() { int main() {
for_transports(test_server_create, "create a modbus server"); for_transports(test_server_create, "create a modbus server");
for_transports(test_server_receive_base, "receive no messages without failing"); 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_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; return 0;
} }

View File

@ -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))); check(mbsn_client_create(&CLIENT, platform_conf_pipe_client(transport)));
mbsn_set_destination_rtu_address(&CLIENT, TEST_SERVER_ADDR); 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_byte_timeout(&SERVER, 100);
mbsn_set_read_timeout(&CLIENT, -1); mbsn_set_read_timeout(&CLIENT, 5000);
mbsn_set_byte_timeout(&CLIENT, 100); mbsn_set_byte_timeout(&CLIENT, 100);
assert(pthread_mutex_lock(&server_stopped_m) == 0); assert(pthread_mutex_lock(&server_stopped_m) == 0);