Coordination between function and string data_event
Rich Abel
Posts: 104
I'm looking for opinions on approaches to coordinating the action of a function which formats and sends commands to a RS232 controlled device and the string data_event that is triggered and parses the response. In prior efforts, I've run into the case where the string buffer contained only a partial or incomplete response when the results were tested. I've read the various posts on the topic of receiving data from serial devices...and decided I'd try the following approach.
[code]
DEFINE_FUNCTION fnSEND_CMD(.....)
{
// build command string
CLEAR_BUFFER cDEV_BUF // start with a clean slate
SEND_STRING dvDEVICE,"'...command string...'" // send out the command
WAIT_UNTIL(nDEV_RESP) // wait till the data_event has done its thing
RETURN cCMD_RESULT // return the results to the calling code. note as written this generates compilier errors.
}
DATA_EVENT[dvDEV]
{
// valid return is 8 or more characters and will contain 'M'...
WHILE((LENGTH_STRING(cDEV_BUF) > 8) AND (FIND_STRING(cDEV_BUF,'M',6)))
{
SELECT // choose response based on ACTIVE (FIND_STRINGS)...
{
ACTIVE : {..........}
ACTIVE : {..........}
etc....asign result to global var cCMD_RESULT
}
PULSE(nDEV_RESP) // signal I'm done
remove parsed command from buffer
}
}
[code]
The WHILE in the data event is intended to keep the data event alive until a complete response is received. The calling function waits for the flag nDEV_RESP to be true, signifying that the data_event has completed parsing and valid data exists in the result variable cCMD_RESULT.
One fatal flaw is that a RETURN inside a WAIT generates a compilier error. I don't want the function to return control to the calling code until the "I'm done flag" nDEV_RESP signals. To avoid the compiler error, I'm currently put the WAIT(nDEV_RESP) immediately following the function in the calling routine, but it would be cleaner and less error prone to make this part of the function itself.
I'd love comments on this approach (and any ideas on how to avoid the 'return-inside-a wait-issue) from any and all.
Thanks,
Rich Abel
ACE Programmer
Cello Technologies
[code]
DEFINE_FUNCTION fnSEND_CMD(.....)
{
// build command string
CLEAR_BUFFER cDEV_BUF // start with a clean slate
SEND_STRING dvDEVICE,"'...command string...'" // send out the command
WAIT_UNTIL(nDEV_RESP) // wait till the data_event has done its thing
RETURN cCMD_RESULT // return the results to the calling code. note as written this generates compilier errors.
}
DATA_EVENT[dvDEV]
{
// valid return is 8 or more characters and will contain 'M'...
WHILE((LENGTH_STRING(cDEV_BUF) > 8) AND (FIND_STRING(cDEV_BUF,'M',6)))
{
SELECT // choose response based on ACTIVE (FIND_STRINGS)...
{
ACTIVE : {..........}
ACTIVE : {..........}
etc....asign result to global var cCMD_RESULT
}
PULSE(nDEV_RESP) // signal I'm done
remove parsed command from buffer
}
}
[code]
The WHILE in the data event is intended to keep the data event alive until a complete response is received. The calling function waits for the flag nDEV_RESP to be true, signifying that the data_event has completed parsing and valid data exists in the result variable cCMD_RESULT.
One fatal flaw is that a RETURN inside a WAIT generates a compilier error. I don't want the function to return control to the calling code until the "I'm done flag" nDEV_RESP signals. To avoid the compiler error, I'm currently put the WAIT(nDEV_RESP) immediately following the function in the calling routine, but it would be cleaner and less error prone to make this part of the function itself.
I'd love comments on this approach (and any ideas on how to avoid the 'return-inside-a wait-issue) from any and all.
Thanks,
Rich Abel
ACE Programmer
Cello Technologies
0
Comments
The "while" is only necassary if you're likely to receive more than one response that needs to be processed in the same code block otherwise if it only returns one response per sent command or query just the the if(find_string(buf,"string",start_at_char_location)) will suffice, although the while shouldn't hurt any.
What actually siginifies the end of a valid response? Show the actual strings it returns including the non print able characters such as CR LF. The main thing is to know when you've received a complete response and then process it. Sometimes that's not so easy.
I always code the "command sends" and the "reply receipts" as entirely separate asynchronous events.
Some devices give no, one or many replies to differing commands, and if there is a comms glitch that becomes unpredictable. So duck that problem by untying them in your code, which means your wait goes away.
Can you elaborate on your comment NMark? As an example, the command to verify an output on an AutoPatch matrix switch returns the input currently assigned to the output. If in my code, I have the task to "listen in" on the audio source being played in one room, I can use the verify output command to discover what input needs to be selected.
Using the approach I outlined, flaws and all, I issue the command via the function and the data event retreives the results and signals completion.
How would you approach this example using your approach?
Thanks. I really appreciate the comments.
VAV: My sketchy example was based upon an AutoPatch Optima matrix switch. I created several functions that correspond to the functions in the AutoPatch command set that I indended to use. My thinking behind the While (( ) AND ( )), was based on coming up with a couple of tests for a result string that would provide reasonable assurance that the result was complete. The responses from some of the commands are variable length and have several possible terminating characters. This was my attempt (which may be lame!) to 'extend the data event' until it contains the complete response.
Thanks again for your thoughts.
Rich
I guess I don't understand why your code needs to ask the box what its state is when surely it is in (a) the state that you told it to be not long ago (b) the state that it last reported to you - ?
This is how I do ALL of my device drivers for non-trivial devices with feedback:
(a) Keep the *known* state of the box in one variable / structure / generic array / whatever. This might include a flag value meaning "Don't Know", (although I don't see how that would arise other than after a boot or perhaps a comms loss or NAK.) This information would usually be derived from feedback from the box or could be faked from commands sent to the box.
(b) Keep the *desired* state in a similar manner. When your system wants to change something in the box, change this.
(c) When communication with the box becomes idle, compare the two states looking for a difference, and upon finding a difference, send the command to rectify the difference.
So in this scenario the three tasks (State monitoring, change of required state, commands to box) happen independently. Because of this you can concentrate on making each task work well in its own little world, rather than worrying about their interactions and timing.
Credit where due: this approach was learned from John Murphy of Auckland NZ.