Pluggable Exception Handler example
-
Create a class that implements MessagingExceptionHandler, and then use it in your call to ModbusMaster.setExceptionHandler().
-
Are there any methods I specifically have to implement in the class that get called when an exception occurs?
Sorry to be such a bother, I just couldn't find much documentation on it (maybe I'm blind).
-
Oh, and I was taking a stab at it and it told me that the class MessagingConnectionHandler couldn't be resolved, so I'm wondering if MessagingConnectionListener is actually the right class, or if I'm using the wrong version of the modbus4j (which I pulled from sourceforge).
Thanks!
-
It's in seroUtils.jar
-
OK, so I implemented all the required methods, and I just had them log error messages for me.
But my problem that I was tackling is still there, and I get no messages in the android log.
So, maybe someone can give me a pointer, but here is what I've seen, and where I've tracked it down to:
I'm polling, about once every second, just reading a set of registers. And it will go along just fine for a while, but at random intervals, it will just stop for about 30 seconds (which is what my timeout is) and then continue.
I put log messages in throughout (breakpoints suck for a random polling problem) and tracked it down to this line in the TcpMaster.java line:
ipResponse = (IpMessageResponse)conn.send(ipRequest);
I put a lot message before and after this line, and the log shows the line before, and then a 30 second pause, and the line after.
Any ideas on what might be causing this? I can't really dig much further because the send method is in seroUtils.jar, otherwise I would just keep chasing it down.
I would think that if I time out the connection for some reason the exception handler class I implemented would spit out a log message, but this hasn't been the case.
Any help is appreciated!
Thanks -
The MessageControl source is below. But i doubt this is going to help. The fact that it pauses for the time value of your timeout indicates that no response was received. The thread waits for a response to be received by the thread that reads the input stream. Either nothing was received, or something was received that didn't qualify as a response.
The kinds of exceptions that are reported by the MessagingConnectionListener are IOException-type messages: in your case, situations such as the socket being disconnected. Response timeouts are thrown to the requesting thread, which i think is reasonable. If you want quicker response from timeout situations, you should use a smaller timeout.
public class MessageControl implements DataConsumer { private static int DEFAULT_RETRIES = 2; private static int DEFAULT_TIMEOUT = 500; public boolean DEBUG = false; private Transport transport; private MessageParser messageParser; private RequestHandler requestHandler; private MessagingExceptionHandler exceptionHandler = new DefaultMessagingExceptionHandler(); private int retries = DEFAULT_RETRIES; private int timeout = DEFAULT_TIMEOUT; private final WaitingRoom waitingRoom = new WaitingRoom(); private final ByteQueue dataBuffer = new ByteQueue(); public void start(Transport transport, MessageParser messageParser, RequestHandler handler) throws IOException { this.transport = transport; this.messageParser = messageParser; this.requestHandler = handler; transport.setConsumer(this); } public void close() { transport.removeConsumer(); } public void setExceptionHandler(MessagingExceptionHandler exceptionHandler) { if (exceptionHandler == null) this.exceptionHandler = new DefaultMessagingExceptionHandler(); else this.exceptionHandler = exceptionHandler; } public int getRetries() { return retries; } public void setRetries(int retries) { this.retries = retries; } public int getTimeout() { return timeout; } public void setTimeout(int timeout) { this.timeout = timeout; } public IncomingResponseMessage send(OutgoingRequestMessage request) throws IOException { return send(request, timeout, retries); } public IncomingResponseMessage send(OutgoingRequestMessage request, int timeout, int retries) throws IOException { byte[] data = request.getMessageData(); if (DEBUG) System.out.println("MessagingControl.send: " + StreamUtils.dumpMessage(data)); IncomingResponseMessage response = null; if (request.expectsResponse()) { WaitingRoomKey key = request.getWaitingRoomKey(); try { // Enter the waiting room waitingRoom.enter(key); do { // Send the request. write(data); // Wait for the response. response = waitingRoom.getResponse(key, timeout); if (DEBUG && response == null) System.out.println("Timeout waiting for response"); } while (response == null && retries-- > 0); } finally { // Leave the waiting room. waitingRoom.leave(key); } if (response == null) throw new TimeoutException("request=" + request); } else write(data); return response; } public void send(OutgoingResponseMessage response) throws IOException { write(response.getMessageData()); } /** * Incoming data from the transport. Single-threaded. */ public void data(byte[] b, int len) { if (DEBUG) System.out.println("MessagingConnection.read: " + StreamUtils.dumpMessage(b, 0, len)); dataBuffer.push(b, 0, len); // There may be multiple messages in the data, so enter a loop. while (true) { // Attempt to parse a message. try { // Mark where we are in the buffer. The entire message may not be in yet, but since the parser // will consume the buffer we need to be able to backtrack. dataBuffer.mark(); IncomingMessage message = messageParser.parseMessage(dataBuffer); if (message == null) { // Nothing to do. Reset the buffer and exit the loop. dataBuffer.reset(); break; } if (message instanceof IncomingRequestMessage) { // Received a request. Give it to the request handler if (requestHandler != null) { OutgoingResponseMessage response = requestHandler .handleRequest((IncomingRequestMessage) message); if (response != null) send(response); } } else // Must be a response. Give it to the waiting room. waitingRoom.response((IncomingResponseMessage) message); } catch (Exception e) { exceptionHandler.receivedException(e); } } } private void write(byte[] data) throws IOException { synchronized (transport) { transport.write(data); } } public void handleIOException(IOException e) { exceptionHandler.receivedException(e); } }
-
So this is pretty crazy. I added conn.DEBUG = true; to the TcpMaster.java, and I stopped getting the error. I ran the code for hours, all morning and into the afternoon, without issue.
I comment that out again, get an error within a minute or two. I've been running it for 15 or 20 minutes now and have 6 of these errors.
I'm just talking to a modbus simulator from the android emulator, so I know there isn't a network issue. Also the simulator says that it received the read request and sends a response even when the error occurs. And it is sending the same response (with the exception of the changed transaction ID) every time (it has a 'console' like mode where it shows the hex of what it is sending and receiving).
I reverted my code to the jamodbus version and put similar debug lines in it and it runs fine as well.
This is a very weird problem now that the debug statement is thrown in the mix. Any ideas on what the debug=true line might be adding into the mix that makes the problem go away?
-
That is pretty crazy. The message dumping just reads the byte array; it doesn't change anything. And i can't see anything like a race condition occurring. Are you sure you're using the latest versions of everything? I've updated everything in the CVS just to be sure.
-
I'm not sure exactly which version I'm running. I pulled the latest source from the release from the sourceforge page towards the end of the last month, and I believe it was the released, not the head. Probably the 1.02 source package.
Is there a CVS repository that would be better to pull from for both the Serio.java file and modbus4j source? Is it just the sourceforge one? If there is, I'll update to that.
As a test today I found a tweak in the modbus simulator I'm testing against that lest me put in delay before a response. I had it set to 1ms in all previous tests. Then today I ran it with the debug=false and a delay of 10, 20, and 30ms - each for about an hour. The 10 and 20 ms tests had 2 errors, but the 30ms delay test is going now for over an hour w/o an error.
-
Try the latest from the CVS. The latest seroUtils.jar is checked in there too. (Yes, at SF.) Otherwise, i can't come up with a good reason for what you're seeing.
-
I'm testing the latest CVS for everything now. I'll let you know how it goes (so far after a few minutes, no errors).
Thanks for all the help!
Ben
-
I have about an hour of testing running and there are no errors!
Thanks a ton for all your help. I'm excited to be past this, as now I can hopefully get this released in some form in the next few weeks (a couple more features to implement).
-
Good to hear. Thanks for following up.