Add modbus_reply_callback() and modbus_set_reply_callback()#408
Add modbus_reply_callback() and modbus_set_reply_callback()#408pboettch wants to merge 1 commit intostephane:masterfrom
Conversation
|
Not sure why travis is failing, it works locally. |
| RTU-slave. In its implementation the user has to check whether the slave-id, _slave_, which was | ||
| decoded from the request, should be answered to or not and returning TRUE if so, otherwise FALSE. | ||
| Returning FALSE will make *modbus_reply_callback()* exit immedialty and return 0. | ||
|
|
There was a problem hiding this comment.
Why is accept_rtu_slave only required for RTU? The address field is also available in TCP requests. I don't see why this should be handled differently?
There was a problem hiding this comment.
But is it used in TCP? To address different devices via one connection? I don't see it being explained in the spec (while flying over it).
There was a problem hiding this comment.
If I remember correctly, it is used e.g. in SMA inverters to have different register mappings available. And think about a TCP to RTU modbus proxy where you want to have one address for the gateway itself and the others mapped to the UART...
There was a problem hiding this comment.
OK, the slave-field in TCP is useful.
The reason for not needing an accept_rtu_slave in a TCP-connection is, that you probably don't want to ignore a request based on the slave-ID when implementing a TCP-server. A TCP-connection is point-to-point whereas a RTU-bus is broadcast, a slave on a RS482-bus sees all the requests coming by, even the ones it doesn't want to handle, so you need to filter - not answering those you don't care for - someone else will do. In TCP no one else will handle the request.
Is that logic correct?
There was a problem hiding this comment.
(but you can still handle it optionally if you want yes? For people making rtu drop in replacements, and that sort of thing...)
| MODBUS_WRITE-function-requests are passed to the write-callback. When encountering the | ||
| MODBUS_FC_WRITE_AND_READ_REGISTERS-function modbus_reply_callback is calling the write and the | ||
| read-callback. | ||
|
|
There was a problem hiding this comment.
I guess a dedicated write_read callback is required here... reason below.
| MODBUS_FC_WRITE_AND_READ_REGISTERS-function modbus_reply_callback is calling the write and the | ||
| read-callback. | ||
|
|
||
| These functions are designed for implementing a Modbus TCP server and RTU slaves. |
There was a problem hiding this comment.
Wording nitpick: better write "...implementing Modbus TCP and RTU slaves." ?
| buf[1] = value1 & 0xff; | ||
| buf[2] = (value2 >> 8) & 0xff; | ||
| buf[3] = value2 & 0xff; | ||
|
|
There was a problem hiding this comment.
Can we just use the helper macros here?
There was a problem hiding this comment.
If they are part of the API, yes. I personally prefer to tell the dirty truth and leave it to the user.
src/modbus.c
Outdated
| } | ||
| } | ||
|
|
||
| // TODO broadcast responses should use the slave-id, probably |
There was a problem hiding this comment.
Broadcasts simply do not have a response by definition.
There was a problem hiding this comment.
Thanks for helping me with this question.
There was a problem hiding this comment.
however, it is a quirk that many devices use, and has been porposed as one of the "quirks_modes" that libmodbus should support using.... (sorry for the necro)
| int req_length); | ||
|
|
||
| /** | ||
| * UTILS FUNCTIONS |
There was a problem hiding this comment.
Some general notes:
-
this PR is very... big 😄 ... I'm not sure that I already understood all parts
-
When implementing a Modbus server, you usually want to do something with the data, i.e. the Modbus server is only one part of the whole application. The servers which I have written were multi-threaded, so you usually need some locking when accessing shared data. I think this can already prepared by adding lock/unlock callbacks.
-
I'm wondering whether extending the modbus context makes sense. As already stated in Added messagetype 0x14 and 0x15 for file-access. #407, I could image a second "layer" on top of the existing "ground or base layer" functions which cares about such higher-level functionality.
There was a problem hiding this comment.
-
Yes it's big, I think to make it easier to review I will split out the reply code in a separate file. I think it is not possible to further split the code in several commits. It's complex.
-
I agree with the fact that you need locking around the data, however I disagree that libmodbus has to take care of this within its API. If you need locking, just lock the
modbus_reply_callback()-call in your code and access to the data is atomic. -
Is the current reply-based-on-mapping-code used in anything else than examples and tests? When thinking about servers and slaves I was very quickly limited by the mapping-implementation, which made me develop this code. I my eyes, the reply-callback-code fits the library's style, no need to create extra layers.
I'm going to write/import examples in the test-dirs which will show how to write a multi-slave-server, that could help to see how to use it.
Thanks a lot for your review! 👍
There was a problem hiding this comment.
Can it be master slave opperate in modbus rtu in PIC18fXXX
There was a problem hiding this comment.
If libmodbus compiles and works for this platform, this callback-based server/slave should work as well.
d113379 to
029ddca
Compare
Based on @frodete's proposal (stephane#319) this implementation adds a a callback-based reply-functionality. The old mapping-implementation is now based on this version. As a nice side-effect some DRY code optimizations could have been done. Also adds a MODBUS_SLAVE_ACCEPT_ALL "address", which can be used with modbus_set_slave(). This makes the modbus_receive()-function accept all requests, independently of the slave-address found in the decoded-request. Useful for multi-slave-implementation using libmodbus.
029ddca to
fdd398e
Compare
|
Endorsement: using this PR in production code; I have read some of the code, it looks good to me. Maybe the interface is a bit too low-level, e.g. I don't like that I need to handle both WRITE_SINGLE_REGISTER and WRITE_MULTIPLE_REGISTERS when the callback signature actually covers both cases, but maybe this flexibility can be useful for some cases. I like the fact that it comes with docu, which seems complete and accurate, although it could profit from being translated into "simple English". I have started doing that, and will finish it if there is interest. |
|
Happy to hear that. Same here. Used happily in two different production environments. One of it really uses it all, especially the key-feature: multiple RTU-slaves. Regarding Thanks for your documentation efforts. Describing all functionalities in a man-pages took me an enormous amount of time if you can simplify it, would be highly appreciated. Happy to pull that in. In my fork at least. |
|
+1 for this PR as well, would be a good point adding support for custom command codes |
|
Why wasn't the pull request merged? Can i help it somehow? |
|
Has there been any more progress on this PR? |
Based on @frodete's proposal (#319) this implementation adds a a callback-based reply-functionality.
The old mapping-implementation is now based on this version. As a nice side-effect some DRY code optimizations could have been done.
Unit tests are OK (locally, not on travis though). And my RTU-multi-slave server works.
EDIT:
My motivation for needing a callback-based reply is that I need to implement a modbus-tcp-server and a modbus-rtu-slave. Both will handle the data within a cache. The rtu-slave implementation has to be able to handle multiple slaves. On one RS485-port multiple slave-id will be handled.
More details to what I have done: I took the
modbus_reply-function and changed its name tomodbus_reply_callback. Then, each time where something was checked against thestart_bits/start_registers-adresses andtab_*-counts I replaced it with a call to the callbackverify. And each time the payload-part of the request or response-buffer was accesses I replaced it with a call to the callbackreadorwriterespectively.Then I implemented the previous
modbus_reply-function in a way that it uses themodbus_reply_callbackfunction by providing callbacks forverify,readandwrite. In these callback-functions I put the code which previously was in charge to fill in the buffer or to verify the boundaries. By doing so, some code-duplicates have been removed, especially accesses to the buffers in the mapping and its verification.Also adds a MODBUS_SLAVE_ACCEPT_ALL "address", which can be used with modbus_set_slave(). This makes the modbus_receive()-function accept all requests, independently of the slave-address found in the decoded-request. Useful for multi-slave-implementation using libmodbus.
I updated the man-pages and added one for modbus_reply_callback(), please review and comment and merge