fangzhchen / nmodbus

Automatically exported from code.google.com/p/nmodbus
0 stars 0 forks source link

Add support for custom messages when using the RTU protocol #47

Open GoogleCodeExporter opened 8 years ago

GoogleCodeExporter commented 8 years ago
When you use the ExecuteCustomMessage method on ModbusMaster the RTU
protocol needs special handling since it the library can not know anything
about the length of the data to receive as response.

This patch adds support for registering the custom messages types by their
function code with a delegate for determining the length of data to receive
for any particular modbus message.

Original issue reported on code.google.com by rasmus.toftdahl.olesen on 19 May 2009 at 1:56

Attachments:

GoogleCodeExporter commented 8 years ago
This is really great! Give me a week or so and I'll integrate.

Original comment by sja...@gmail.com on 20 May 2009 at 12:12

GoogleCodeExporter commented 8 years ago
Have a look at the changes I made to achieve RTU custom message support and 
slave 
support for custom messages in all protocol flavors 
http://code.google.com/p/nmodbus/source/detail?r=321

I opted to create an additional interface IModbusMessageRtu which extends the 
IModbusMessage interface and adds two delegate properties for calculating RTU 
message 
length. This implementation avoids any global registration for using 
ModbusMaster 
ExecuteCustomMessage.

The slave also now supports custom messages but requires registration. For 
example -

try
{
    Slave.RegisterCustomFunction<CustomFoobarRequest>(CustomFoobarRequest.FoobarF
unctionCode,
        (request, dataStore) =>
        {
            return new CustomFoobarResponse 
            { 
                SlaveAddress = request.SlaveAddress, 
                ByteCount = (byte) (request.NumberOfPoints * 2), 
                Data = 
dataStore.HoldingRegisters.Slice(request.StartAddress, 
request.NumberOfPoints).ToArray() 
            };
        });             

    var foobarRequest = new CustomFoobarRequest(SlaveAddress, 104, 2);
    var foobarResponse = 
Master.ExecuteCustomMessage<CustomFoobarResponse>(foobarRequest);
}
finally
{
    Slave.UnregisterCustomFunction(CustomFoobarRequest.FoobarFunctionCode);
}

Thanks for your help with this and let me know if you have any comments.

Original comment by sja...@gmail.com on 22 May 2009 at 9:52

GoogleCodeExporter commented 8 years ago
I haven't tested it yet but it certainly seems to be correct.

I especially like the FunctionalUtility.Memoize() stuff in CustomMessageInfo - 
it
almost looks Pythonic.

We are still on 2.0 in our project, but it is very nice to see that 3.0 really 
opens
up functional programming to C# developers.

I will write back once i get this tested (likely in the start of coming week).

Original comment by rasmus.toftdahl.olesen on 22 May 2009 at 1:07

GoogleCodeExporter commented 8 years ago
FunctionalUtility is available at http://code.google.com/p/unme/

Original comment by sja...@gmail.com on 22 May 2009 at 5:44

GoogleCodeExporter commented 8 years ago
I have now tried it in a scenario with a real life Modbus device, and 
functionality
wise it works great.

I have a few comments though:

Could we please drop the System.Func<> stuff in IModbusMessageRtu, it forces me 
to
reference System.Core.dll which is a 3.5 DLL, and i would really prefer running 
in a
purely 2.0 environment. A delegates works just as great and maintains 2.0 
compatibility.

IModbusMessageRtu both has a RtuRequestBytesRemaining and 
RtuResponseBytesRemaining,
isn't this unnecessary, if the request and response are different you need to
implement two separate classes anyway. Otherwise the
IModbusMessage.Initialize(byte[]) method wouldn't know if it was reading a 
request or
a response anyway.

Other than that I think it is nicely done, though i can't shake the feeling that
having to instantiate an instance of the message to call the measuring methods 
isn't
right, but to fix it would require a greater deal of refactoring so that is a
non-issue in my oppinion.

Keep up the good work! 

Original comment by rasmus.toftdahl.olesen on 27 May 2009 at 12:53

GoogleCodeExporter commented 8 years ago
Oh, just one more thing, you might want to modify the comment in ModbusSlave.cs 
about
considering narrowing the allowed function codes to 65-101, upon closer 
inspection of
the modbus spec it looks like the allowed user-defined range is 65-72 and 
100-110.

Original comment by rasmus.toftdahl.olesen on 27 May 2009 at 1:00

GoogleCodeExporter commented 8 years ago
Regarding 2.0 - I will FI this change to the 2.0 branch, you should be
able to pick up that release and run w/o the System.Core dll.

Regarding the new interface – excellent point, I will remove
RtuRequestBytesRemaining and RtuResponseBytesRemaining and replace
with RtuBytesRemaining.

Will modify the comment, thanks for the heads up.

Regarding the clunky custom message creation - one nice thing you
could provide is extension methods for each custom message which would
hide the API consumer from the message instantiation (you’ll have to
use 3.5 though, unless you do a workaround like this -
http://geekswithblogs.net/robp/archive/2007/12/20/using-extension-methods-in-.ne
t-2.0-with-visual-studio-2008.aspx).

public static class CustomMessageExtensions
{
       public static ushort[] ReadFoobar(this IModbusMaster master, byte
slaveAddress, ushort startAddress, ushort numberOfPoints)
       {
               var foobarRequest = new CustomFoobarRequest(slaveAddress,
startAddress, numberOfPoints);
               var foobarResponse =
master.ExecuteCustomMessage<CustomFoobarResponse>(foobarRequest);

               return foobarResponse.Data;
       }
}
…
var data = Master.ReadFoobar(SlaveAddress, 104, 2);

Ideally how do you want the API to look? I’m happy to do the
refactoring if there is a better way.

Thanks a lot for the patch and feedback!
Scott

Original comment by sja...@gmail.com on 28 May 2009 at 2:32

GoogleCodeExporter commented 8 years ago
Oh, the message creation is all fine, i was just mumbling about the extra modbus
message instance that we have to create in order for us to have the 
RtuBytesAvailable
method available for use. This is what the FunctionalUtility.Memoize() stuff 
helps with.

The only not to have it would be to separate the RtuBytesAvailable method 
registered
somewhere (like the ModbusFactory) so that it could be used before the message 
is
fully received. But, i like that the RtuBytesAvailable method is on the
IModbusRtuMessage interface, it makes it much easier to identify the
RtuBytesAvailable function for a particular message.

I guess what i am saying is that your design is a nice trade-off, it puts user
friendlyness over the hardcore software design principles.

I haven't tried the new code yet (my laptop had a fit so i am currently trying 
to
recover without having to reinstall), but from what i can see here it looks 
good to go.

Original comment by rasmus.toftdahl.olesen on 29 May 2009 at 11:25

GoogleCodeExporter commented 8 years ago
Do you maintain the 2.0 and 3.5 code bases completely separately? Or is this 
some
shared files between them?

Original comment by rasmus.toftdahl.olesen on 2 Jun 2009 at 10:22

GoogleCodeExporter commented 8 years ago
They are completely separate.
http://code.google.com/p/nmodbus/source/browse/#svn/branches/NModbus_net-2.0

It's a real pain in the ass to have two branches and the 3.5 version
really doesn't add any value, but I wanted to play with 3.5 and there
was a still demand for a 2.0 version...  My plan is to move the trunk
to 4.0 but always keep the 2.0 branch up to date.

I still haven't gotten around to FI'ing the rtu changes to the 2.0 branch.

Original comment by sja...@gmail.com on 2 Jun 2009 at 2:05

GoogleCodeExporter commented 8 years ago
Ok, i appreciate that you still keep the 2.0 version. Hopefully we will all 
ditch 2.0
some time in the future - and you can stop playing doing this tedious work.

Oh, and by the way: FI'ing? Fixing? - Fixing/Integrating?

Original comment by rasmus.toftdahl.olesen on 2 Jun 2009 at 2:28

GoogleCodeExporter commented 8 years ago
Sorry for the jargon, Forward Integration - 
http://blogs.msdn.com/larryosterman/archive/2005/02/01/364840.aspx

Original comment by sja...@gmail.com on 2 Jun 2009 at 2:32

GoogleCodeExporter commented 8 years ago
Never heard that term before, you live you learn i guess :)

Original comment by rasmus.toftdahl.olesen on 2 Jun 2009 at 3:36