Proper Usage of io.intercept and passing data back to <Incoming>

In my plug-in I’ll be passing commands to a serial interface, and typically those commands get echoed back confirming that the status was set as requested.

I will have code in my implementations top level “incoming” section to handle data as it comes from the serial device in the general case. (Example: When someone physically flips on a light switch, the serial device reports the pertinent information related to the action.)

My plan is to create a function in which I pass the actual command to send and what (if any) confirmation text I should receive echoed back. The function would return:
0 : command not successfully sent
1 : command sent, but not confirmed
2 : command sent and confirmed

My main questions center around 2 things:

  1. The usage of io.intercept . I know I should call it prior to issuing the io.write (so vera buffers any incoming messages). However, how do I turn “off” the intercept? Is the intercept only good for a single io.read? (And must be called again after a read if I want to read again?)

  2. On the case where I perform an io.read, and it does NOT contain the confirmation text I’m expecting, I want to pass it back to the general block to process. How do I go about doing this?

Following is the bulk of what will be the function…for now I will simply attempt to read the buffer 10 times until I either read the confirmation string, or not:

[code]Function sendLGcommand (commandString, confirmString)
luup.io.intercept()
if(luup.io.write(commandString) == false) then
luup.log( “Cannot send '” … tostring(commandString) …“'.”, 1)
return 0
end
if confirmString = nil or confirmString=“” then
return 1
end if
local tries = 0
local confirmed = 1
while(confirmed ~=2 and tries < 10) do
incoming_data=luup.io.read()
tries = (tries+1)
if (incoming_data) ~= nil then
i =string.find(incoming_data,confirmString)
If i ~= nil then
confirmed=2
else
[CODE TO RETURN INCOMING_DATA TO THE GENERAL INCOMING DATA HANDLDER]
luup.io.intercept() (Is this needed?)
end if

	end if			
end
return confirmed				

end [/code]

Hi, JoeyD,

1) The usage of io.intercept . I know I should call it prior to issuing the io.write (so vera buffers any incoming messages). However, how do I turn "off" the intercept? Is the intercept only good for a single io.read? (And must be called again after a read if I want to read again?)

Although some of the most valuable forum members have another opinion, I can confirm that the “luup.io.intercept” works as described in the Wiki.
If you call “luup.io.intercept” just before you call “luup.io.write”, all the data coming in as the result of the “luup.io.write” will be buffered so that you can read it using “luup.io.read”.
Another call of “luup.io.write” will cancel the “luup.io.intercept”, and the incoming data will not be buffered, but will be directed to the “incoming job” and can be handled there.

2) On the case where I perform an io.read, and it does NOT contain the confirmation text I'm expecting, I want to pass it back to the general block to process. How do I go about doing this?

Well, this is something I have never tested for reliability, but during testing a new plug-in of my own, I have noticed that if you use “luup.io.write” with a single “dummy” byte (something like ) right after the first “luup.io.read”, further data form another “luup.io.write” is recieved via the general block.
However, to my opinion it is not possible to first check the incoming data using “luup.io.read” and then redirect this data to the block.
Question is, why should that be necessary?

Hope this helps.

Part of the problem with luup.io.intercept() is that it’s full of race conditions by design. If you’re communicating with a serial device that sends its response in bursts, then you may only get part of the response in luup.io.read(). Sure, you can call luup.io.intercept() again, but there’s a window in between where the serial device might have tried to send more data. Where that data goes is unspecified.

Every plugin that I’ve written that uses a serial connection I’ve eventually given up on using luup.io.intercept() and ported it to a state machine with transitions controlled by handlers. Yes, a state machine adds complexity. But your plugin will probably evolve through the same stages of development and you’ll end up doing the same thing. Save yourself refactoring the code later by designing in the state machine now.

For example code, see my Caddx alarm plugin. The general idea is:

[ul][li]When the plugin receives an action from a scene or UI, the implementation file’s code sends the outgoing serial data, and adds to a global table that I’m expecting a response. Then it returns.[/li]
[li]Handle all serial responses through the section (there is no luup.io.read()). For each response, act on it, then remove it from the “expected” table if it was there.[/li][/ul]

Hi, Snotneus,

Thanks! (I had not understood that issuing a second io.write effectively turns off io.intercept.)

[quote=“Snotneus, post:2, topic:184600”]However, to my opinion it is not possible to first check the incoming data using “luup.io.read” and then redirect this data to the block.
Question is, why should that be necessary?[/quote]

I don’t know enough about how my serial device works (need to experiment more), but I suppose it is possible that this scenario can happen:

  1. I send an io.write command at the same time someone alters a physical device.
  2. What does the serial device report first? Reports the status of the physical change, or the response to my write?

In other words, I assume it’s possible that the first piece of data coming from the serial device is not actually the reponse to my write…and if it’s not, I’d want it to be handled by the general block.

Thanks for the suggestion. That does seem a more robust way to handle it.

My device is lighting…not as mission critical as something like an alarm, or door locks or sprinklers. For now I will likely hold off on implementing a state machine as I’m rapidly trying to learn everything else and get a more fully functioning plug-in working. Worst case for me is that in any corner cases I might “miss” a lighting update and my UI won’t properly reflect the status of a light. Not something I’d like in a finished product, won’t cause me to think my alarm is not properly armed, or my sprinklers are not turned off… :wink:

You’ve just presented a watertight argument for separating the sending of the request and the processing of the response. If your serial device does behave this way (IMO it must) then all received data is theoretically asynchronous and your plugin may as well handle it all in .

@JoeyD,

1) I send an io.write command at the same time someone alters a physical device. 2) What does the serial device report first? Reports the status of the physical change, or the response to my write?

The “luup.io.intercept” only works for data traffic initiated by your plug-in.
If you suspect data traffic initiated from another device, you are lost and you have to use the block.
If you carefully read the Wiki regarding the “luup.io.intercept” you will understand that the “luup.io.intercept” is not designed for handling such data traffic.
I think in that case would be best to go the way futzle suggested.

[quote author=Snotneus link=topic=28853.msg206825#msg206825 date=1418278905]
@JoeyD,

]If you carefully read the Wiki regarding the "luup.io.intercept" you will understand that the "luup.io.intercept" is not designed for handling such data traffic....

Lol…I thought I had read it. To the point where it said

If the device you’re communicating with sends unsolicited data then there’s the risk that the data you read is not the response you’re looking for. If so, you can manually pass the response packet to the incoming data handler.

Which is what ultimately brought me to posting this thread. :slight_smile: In other words, the wiki to me makes it sound like you can use intercept in conjunction with asynchronous I/o.

But in any case, I am proceeding as suggested, and just handling everything in . Thanks both of you for your input!