Commit f0db03dd4816b26cd55432aac820c61eeeccd96b

Authored by Stéphane Raimbault
1 parent b0c05667

New quirks handler (closes #38 #533)

Useful functions when you are confronted with equipment which does
not respect the protocol, which behaves strangely or when you wish
to move away from the standard.

Thank you @mhei for the great initial version.
docs/index.md
... ... @@ -251,6 +251,11 @@ error codes into error message strings; for details refer to
251 251  
252 252 ## Miscellaneous
253 253  
  254 +To deviate from the Modbus standard, you can enable or disable quirks with:
  255 +
  256 +- [modbus_disable_quirks](modbus_disable_quirks.md)
  257 +- [modbus_enable_quirks](modbus_enable_quirks.md)
  258 +
254 259 The `_LIBMODBUS_VERSION_STRING_` constant indicates the libmodbus version the
255 260 program has been compiled against. The variables 'libmodbus_version_major',
256 261 'libmodbus_version_minor', 'libmodbus_version_micro' give the version the
... ...
docs/modbus_disable_quirks.md 0 → 100644
  1 +# modbus_disable_quirks
  2 +
  3 +## Name
  4 +
  5 +modbus_disable_quirks - disable a list of quirks according to a mask
  6 +
  7 +## Synopsis
  8 +
  9 +```c
  10 +int modbus_disable_quirks(modbus_t *ctx, unsigned int quirks_mask);
  11 +```
  12 +
  13 +## Description
  14 +
  15 +The function shall disable the quirks according to the provided mask. It's
  16 +useful to revert changes applied by a previous call to
  17 +[modbus_enable_quirks](modbus_enable_quirks.md)
  18 +
  19 +To reset all quirks, you can use the specific value `MODBUS_QUIRK_ALL`.
  20 +
  21 +```c
  22 +modbus_enable_quirks(ctx, MODBUS_QUIRK_MAX_SLAVE | MODBUS_QUIRK_REPLY_TO_BROADCAST);
  23 +
  24 +...
  25 +
  26 +// Reset all quirks
  27 +modbus_disable_quirks(ctx, MODBUS_QUIRK_ALL);
  28 +```
  29 +
  30 +## Return value
  31 +
  32 +The function shall return 0 if successful. Otherwise it shall return -1 and set
  33 +errno.
  34 +
  35 +## See also
  36 +
  37 +- [modbus_enable_quirks](modbus_enable_quirks.md)
... ...
docs/modbus_enable_quirks.md 0 → 100644
  1 +# modbus_enable_quirks
  2 +
  3 +## Name
  4 +
  5 +modbus_enable_quirks - enable a list of quirks according to a mask
  6 +
  7 +## Synopsis
  8 +
  9 +```c
  10 +int modbus_enable_quirks(modbus_t *ctx, unsigned int quirks_mask);
  11 +```
  12 +
  13 +## Description
  14 +
  15 +The function is only useful when you are confronted with equipment which does
  16 +not respect the protocol, which behaves strangely or when you wish to move away
  17 +from the standard.
  18 +
  19 +In that case, you can enable a specific quirk to workaround the issue, libmodbus
  20 +offers the following flags:
  21 +
  22 +- `MODBUS_QUIRK_MAX_SLAVE` allows slave adresses between 247 and 255.
  23 +- `MODBUS_QUIRK_REPLY_TO_BROADCAST` force a reply to a broacast request when the
  24 + device is a slave in RTU mode (should be enabled on the slave device).
  25 +
  26 +You can combine the flags by using the OR logical operator.
  27 +
  28 +## Return value
  29 +
  30 +The function shall return 0 if successful. Otherwise it shall return -1 and set
  31 +errno.
  32 +
  33 +## See also
  34 +
  35 +- [modbus_disable_quirks](modbus_disable_quirks.md)
... ...
src/modbus-private.h
... ... @@ -96,6 +96,7 @@ struct _modbus {
96 96 int s;
97 97 int debug;
98 98 int error_recovery;
  99 + int quirks;
99 100 struct timeval response_timeout;
100 101 struct timeval byte_timeout;
101 102 struct timeval indication_timeout;
... ...
src/modbus-rtu.c
... ... @@ -91,8 +91,10 @@ static const uint8_t table_crc_lo[] = {
91 91 * internal slave ID in slave mode */
92 92 static int _modbus_set_slave(modbus_t *ctx, int slave)
93 93 {
  94 + int max_slave = (ctx->quirks & MODBUS_QUIRK_MAX_SLAVE) ? 255 : 247;
  95 +
94 96 /* Broadcast address is 0 (MODBUS_BROADCAST_ADDRESS) */
95   - if (slave >= 0 && slave <= 247) {
  97 + if (slave >= 0 && slave <= max_slave) {
96 98 ctx->slave = slave;
97 99 } else {
98 100 errno = EINVAL;
... ...
src/modbus-tcp.c
... ... @@ -76,8 +76,10 @@ static int _modbus_tcp_init_win32(void)
76 76  
77 77 static int _modbus_set_slave(modbus_t *ctx, int slave)
78 78 {
  79 + int max_slave = (ctx->quirks & MODBUS_QUIRK_MAX_SLAVE) ? 255 : 247;
  80 +
79 81 /* Broadcast address is 0 (MODBUS_BROADCAST_ADDRESS) */
80   - if (slave >= 0 && slave <= 247) {
  82 + if (slave >= 0 && slave <= max_slave) {
81 83 ctx->slave = slave;
82 84 } else if (slave == MODBUS_TCP_SLAVE) {
83 85 /* The special value MODBUS_TCP_SLAVE (0xFF) can be used in TCP mode to
... ...
src/modbus.c
No preview for this file type
src/modbus.h
... ... @@ -176,6 +176,14 @@ typedef enum
176 176 MODBUS_ERROR_RECOVERY_PROTOCOL = (1<<2)
177 177 } modbus_error_recovery_mode;
178 178  
  179 +typedef enum
  180 +{
  181 + MODBUS_QUIRK_NONE = 0,
  182 + MODBUS_QUIRK_MAX_SLAVE = (1<<1),
  183 + MODBUS_QUIRK_REPLY_TO_BROADCAST = (1<<2),
  184 + MODBUS_QUIRK_ALL = 0xFF
  185 +} modbus_quirks;
  186 +
179 187 MODBUS_API int modbus_set_slave(modbus_t* ctx, int slave);
180 188 MODBUS_API int modbus_get_slave(modbus_t* ctx);
181 189 MODBUS_API int modbus_set_error_recovery(modbus_t *ctx, modbus_error_recovery_mode error_recovery);
... ... @@ -237,6 +245,8 @@ MODBUS_API int modbus_reply(modbus_t *ctx, const uint8_t *req,
237 245 int req_length, modbus_mapping_t *mb_mapping);
238 246 MODBUS_API int modbus_reply_exception(modbus_t *ctx, const uint8_t *req,
239 247 unsigned int exception_code);
  248 +MODBUS_API int modbus_enable_quirks(modbus_t *ctx, unsigned int quirks_mask);
  249 +MODBUS_API int modbus_disable_quirks(modbus_t *ctx, unsigned int quirks_mask);
240 250  
241 251 /**
242 252 * UTILS FUNCTIONS
... ...
tests/unit-test-client.c
... ... @@ -451,9 +451,26 @@ int main(int argc, char *argv[])
451 451 printf("* modbus_write_registers: ");
452 452 ASSERT_TRUE(rc == -1 && errno == EMBMDATA, "");
453 453  
454   - /** SLAVE REPLY **/
  454 + /** SLAVE ADDRESS **/
455 455 old_slave = modbus_get_slave(ctx);
456 456  
  457 + printf("\nTEST SLAVE ADDRESS:\n");
  458 +
  459 + printf("1/2 Not compliant slave address is refused: ");
  460 + rc = modbus_set_slave(ctx, 248);
  461 + ASSERT_TRUE(rc == -1, "Slave address of 248 shouldn't be allowed");
  462 +
  463 + printf("2/2 Not compliant slave address is allowed: ");
  464 + modbus_enable_quirks(ctx, MODBUS_QUIRK_MAX_SLAVE);
  465 + rc = modbus_set_slave(ctx, 248);
  466 + ASSERT_TRUE(rc == 0, "Not compliant slave address should have been accepted");
  467 +
  468 + modbus_disable_quirks(ctx, MODBUS_QUIRK_MAX_SLAVE);
  469 + rc = modbus_set_slave(ctx, old_slave);
  470 + ASSERT_TRUE(rc == 0, "Uanble to restore slave value")
  471 +
  472 + /** SLAVE REPLY **/
  473 +
457 474 printf("\nTEST SLAVE REPLY:\n");
458 475 modbus_set_slave(ctx, INVALID_SERVER_ID);
459 476 rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS,
... ...