Commit f0db03dd4816b26cd55432aac820c61eeeccd96b
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.
Showing
9 changed files
with
142 additions
and
6 deletions
docs/index.md
| @@ -251,6 +251,11 @@ error codes into error message strings; for details refer to | @@ -251,6 +251,11 @@ error codes into error message strings; for details refer to | ||
| 251 | 251 | ||
| 252 | ## Miscellaneous | 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 | The `_LIBMODBUS_VERSION_STRING_` constant indicates the libmodbus version the | 259 | The `_LIBMODBUS_VERSION_STRING_` constant indicates the libmodbus version the |
| 255 | program has been compiled against. The variables 'libmodbus_version_major', | 260 | program has been compiled against. The variables 'libmodbus_version_major', |
| 256 | 'libmodbus_version_minor', 'libmodbus_version_micro' give the version the | 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,6 +96,7 @@ struct _modbus { | ||
| 96 | int s; | 96 | int s; |
| 97 | int debug; | 97 | int debug; |
| 98 | int error_recovery; | 98 | int error_recovery; |
| 99 | + int quirks; | ||
| 99 | struct timeval response_timeout; | 100 | struct timeval response_timeout; |
| 100 | struct timeval byte_timeout; | 101 | struct timeval byte_timeout; |
| 101 | struct timeval indication_timeout; | 102 | struct timeval indication_timeout; |
src/modbus-rtu.c
| @@ -91,8 +91,10 @@ static const uint8_t table_crc_lo[] = { | @@ -91,8 +91,10 @@ static const uint8_t table_crc_lo[] = { | ||
| 91 | * internal slave ID in slave mode */ | 91 | * internal slave ID in slave mode */ |
| 92 | static int _modbus_set_slave(modbus_t *ctx, int slave) | 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 | /* Broadcast address is 0 (MODBUS_BROADCAST_ADDRESS) */ | 96 | /* Broadcast address is 0 (MODBUS_BROADCAST_ADDRESS) */ |
| 95 | - if (slave >= 0 && slave <= 247) { | 97 | + if (slave >= 0 && slave <= max_slave) { |
| 96 | ctx->slave = slave; | 98 | ctx->slave = slave; |
| 97 | } else { | 99 | } else { |
| 98 | errno = EINVAL; | 100 | errno = EINVAL; |
src/modbus-tcp.c
| @@ -76,8 +76,10 @@ static int _modbus_tcp_init_win32(void) | @@ -76,8 +76,10 @@ static int _modbus_tcp_init_win32(void) | ||
| 76 | 76 | ||
| 77 | static int _modbus_set_slave(modbus_t *ctx, int slave) | 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 | /* Broadcast address is 0 (MODBUS_BROADCAST_ADDRESS) */ | 81 | /* Broadcast address is 0 (MODBUS_BROADCAST_ADDRESS) */ |
| 80 | - if (slave >= 0 && slave <= 247) { | 82 | + if (slave >= 0 && slave <= max_slave) { |
| 81 | ctx->slave = slave; | 83 | ctx->slave = slave; |
| 82 | } else if (slave == MODBUS_TCP_SLAVE) { | 84 | } else if (slave == MODBUS_TCP_SLAVE) { |
| 83 | /* The special value MODBUS_TCP_SLAVE (0xFF) can be used in TCP mode to | 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,6 +176,14 @@ typedef enum | ||
| 176 | MODBUS_ERROR_RECOVERY_PROTOCOL = (1<<2) | 176 | MODBUS_ERROR_RECOVERY_PROTOCOL = (1<<2) |
| 177 | } modbus_error_recovery_mode; | 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 | MODBUS_API int modbus_set_slave(modbus_t* ctx, int slave); | 187 | MODBUS_API int modbus_set_slave(modbus_t* ctx, int slave); |
| 180 | MODBUS_API int modbus_get_slave(modbus_t* ctx); | 188 | MODBUS_API int modbus_get_slave(modbus_t* ctx); |
| 181 | MODBUS_API int modbus_set_error_recovery(modbus_t *ctx, modbus_error_recovery_mode error_recovery); | 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,6 +245,8 @@ MODBUS_API int modbus_reply(modbus_t *ctx, const uint8_t *req, | ||
| 237 | int req_length, modbus_mapping_t *mb_mapping); | 245 | int req_length, modbus_mapping_t *mb_mapping); |
| 238 | MODBUS_API int modbus_reply_exception(modbus_t *ctx, const uint8_t *req, | 246 | MODBUS_API int modbus_reply_exception(modbus_t *ctx, const uint8_t *req, |
| 239 | unsigned int exception_code); | 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 | * UTILS FUNCTIONS | 252 | * UTILS FUNCTIONS |
tests/unit-test-client.c
| @@ -451,9 +451,26 @@ int main(int argc, char *argv[]) | @@ -451,9 +451,26 @@ int main(int argc, char *argv[]) | ||
| 451 | printf("* modbus_write_registers: "); | 451 | printf("* modbus_write_registers: "); |
| 452 | ASSERT_TRUE(rc == -1 && errno == EMBMDATA, ""); | 452 | ASSERT_TRUE(rc == -1 && errno == EMBMDATA, ""); |
| 453 | 453 | ||
| 454 | - /** SLAVE REPLY **/ | 454 | + /** SLAVE ADDRESS **/ |
| 455 | old_slave = modbus_get_slave(ctx); | 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 | printf("\nTEST SLAVE REPLY:\n"); | 474 | printf("\nTEST SLAVE REPLY:\n"); |
| 458 | modbus_set_slave(ctx, INVALID_SERVER_ID); | 475 | modbus_set_slave(ctx, INVALID_SERVER_ID); |
| 459 | rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS, | 476 | rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS, |