Adventures with the AbstractDriver class

I decided to take a crack at extending from AbstractDriver, but I’m a little unsure as to which methods are responsible for what behavior. This is what I believe to be true:

Browse, readItems, and writeItems control the browse, asynchronous read, and asynchronous write behaviors, respectively. They do this by calling createBrowseRequest, createReadRequest, etc. The request is sent to the driven device using postRequest, called from browse, readItems, etc. PostRequest calls messageArrived, which calls getRequestKey. The key is used to marry the response to the correct request.

Now what? How does control get back to browse, readItems, and writeItems?

What is called when we need to get the values of subscribed items?

Is resubscribe my opportunity to create and cancel subscriptions with my device?

Also, can I use nextGuid to generate my transaction ID?

Does Ignition use getRequestKey on both request and response messages?

Now what? How does control get back to browse, readItems, and writeItems?

Control comes back whenever your Request implementations are done. Typically, you would pass the list of read or write items to your request object, which upon receiving a response would then set values on those write items, completing the async call.

What is called when we need to get the values of subscribed items?

Subscribed items are just read requests that happen repeatedly. AbstractDriver will handle keeping these going, you just need to give it read or write requests when it asks for them (createReadRequest(), createSubscribeRequest(), createWriteRequest()).

Also, can I use nextGuid to generate my transaction ID?

The transaction ID is usually tied to whatever protocol you're dealing with, but if not I suppose you could, but it'd be easier to just use an AtomicLong or something that increments sequentially.

Does Ignition use getRequestKey on both request and response messages?

Only response messages. Your Request object will indicate which key it sent with in getKey(), and then your driver implementation needs to be able to extract a key from the response that arrives on the wire so it can be matched up with the request.

You can see all of this in action in the Modbus driver source code included with the SDK.

In the Modbus driver, I can see where createBrowseRequest calls browseOp.browseDone, completing the browse operation. For read and write, however, the read and write messages are returned. I can’t see where ReadItem.setValue() is called, however.

My driver has to talk to its device via SOAP, so when a message is sent, a response is immediately provided. I just can’t figure out what method I have to override to parse that response…

[quote="lisawas"]In the Modbus driver, I can see where createBrowseRequest calls browseOp.browseDone, completing the browse operation. For read and write, however, the read and write messages are returned. I can't see where ReadItem.setValue() is called, however.
[/quote]

If you look at, for example, ReadHoldingRegistersRequest, and follow the response handling that happens in handleResponse(), you'll see that the ReadItems are eventually getting setValue() called on them.

This is probably going to be a little awkward, since pulling a "key" out of a SOAP response won't make much sense.

In Request#sendMessage() you'll probably make your web service call, get the response, then have to do something hacky like call out to AbstractDriver#messageArrived() (taking care to do so in another thread), so that the AbstractDriver machinery can then ask your implementation for the key, pair it up with the request, and call Request#handleResponse().

Unfortunately AbstractDriver was really written with the kind of basic exchange that gets made with a PLC in mind, and the abstraction kind of falls apart when you already have a client or protocol library of some sort that can make requests/responses on its own.

The straight Driver interface is sounding more and more appropriate…I was trying to save myself from having to handle the subscriptions, but I might be better off dealing with that manually. Thank you for the info. I’ll take another crack at it.

Yep, subscriptions are the hardest part about adding a driver, that’s for sure.

If it makes you feel better, I’m not even using AbstractDriver any more for new driver implementations. I favor implementing the Driver interface and using a separately developed client/protocol library for communications. Doing that means your driver is basically glue code + subscription logic.

I’ve been struggling with the same issues trying to write a module with an external library. It would be great if there were another example provided that just used the straight driver class, or even the AbstractDriver. It’s very difficult to figure out the flow of messages. That would also be a great addition to the programmer’s guide!

+1 for an example in the SDK,
I use AbstractDriver for some basic driver implementation,
but I would like to be able to manage with it dynamic item creation when there are subscribed by the opc-ua client.