Why doesn't this work?

I attempting to create a good way to timeout during a cofunction. While doing so, I ran across some strange behavior of DelayMs (And also DelaySec). My question is at the end of this posting.

First, here’s my code:
// File: timeoutTest.c
// Description: test Rabbit cofunctions and timeouts
//
// Original Author: Eric McRae
#use “BLxS2xx.lib”
#use “stddefs.h”

// Function: tooLongTaskV
// Description: runs too long and aborts
// Arguments: none
// Returns: void
// Notes: cofunction
cofunc void
tooLongTaskV( void )
{
u32 startTimeL; // time this function was started
u8 bailB; // flag to signal timeout
int rtn; // return value from DelayMs

everytime			// run these statement on every continuation pass
{
printf("tooLong continued at %d

", MS_TIMER);
if( (MS_TIMER - startTimeL) > 1000 )
{ // If we’ve been continuing for more than a second
bailB = 1; // set the bail flag
printf("tooLong is bailing at %d
", MS_TIMER);
}
}

startTimeL = MS_TIMER;	// capture the starting time of the function
bailB = 0;			// clear the bail flag
printf("TooLong starting at %d

", MS_TIMER);

while( (rtn = DelayMs(3000)) == 0 )
{				// spin waiting for Delay to eat up 3 seconds
if( bailB )		// but if we see the bail flag
{
    printf("TooLong aborting at %d

", MS_TIMER);
abort; // bail out
}
}

// only get here if DelayMs expired
printf("tooLong finishing because DelayMs returned %d at %d

", rtn, MS_TIMER);
}

// Mainline starts here
void main()
{
int i, rtn;

// Initialize the controller
brdInit();

printf ("Starting timeout Test at %d

", MS_TIMER);

while( 1 )
{
costate			// just one costate - loop on long task.
{
    rtn = waitfordone tooLongTaskV();
    printf("waitfordone returned %d

", rtn );
}
}
}

Here’s what gets printed:
Starting timeout Test at 50
TooLong starting at 77
tooLong finishing because DelayMs returned 1 at 88
waitfordone returned 1
TooLong starting at 129
tooLong finishing because DelayMs returned 1 at 145
waitfordone returned 1
TooLong starting at 186
tooLong finishing because DelayMs returned 1 at 202
waitfordone returned 1
TooLong starting at 242
tooLong finishing because DelayMs returned 1 at 258
waitfordone returned 1
TooLong starting at 300
tooLong finishing because DelayMs returned 1 at 316
waitfordone returned 1

Note that DelayMs(3000) seems to be returning true after just 10 - 15 mSec.
I tried it with DelaySec(3) and got the same behavior.

What I expected was to get lots of output from the everytime block until 1 second expired and then the function returns. If change the while … DelayMS to while(1) and add a yield if bailB is not true, that’s in fact what happens.

Why is DelayMs() returning non-zero immediately?

I think you have a classic C semantic error:

while( (rtn = DelayMs(3000)) == 0 )

the “statement”: (rtn = DelayMs(3000)) is an assignment statement and always returns a true (non zero) (it is “true” you made an assignment) then you compare that with the while statement to “==0” and as such, “always returns a true” does not equal a zero, so the while fails.

[QUOTE=jleslie48;4483]I think you have a classic C semantic error:

while( (rtn = DelayMs(3000)) == 0 )

the “statement”: (rtn = DelayMs(3000)) is an assignment statement and always returns a true (non zero) (it is “true” you made an assignment) then you compare that with the while statement to “==0” and as such, “always returns a true” does not equal a zero, so the while fails.[/quote]

ahh, that might be all a bunch of horse manure, rtn = DelayMs(3000) should equate to a 0 if the timer has not run out. I’m not sure if the context of the while or the fact that its inside a pair of ( ) might change that.

Although the Dynamic C guide shows as an example of ~sleep ~ to be :

waitfor(delayMs(3000));

as a proper procedure.

In C, the value of an assignment is always the value of the leftmost operand after the inside assignment takes place. If this was not the case, then the very common test:

if( (c = getchar()) != EOF )

would never work.

Since the above idiom appears in quite a few of the Sample code files, I believe that (a) DC is holding to the standard, and (b) that is not the problem.

What I’m trying to do is figure out a way to timeout on a serial operation. The problem with waitfor( DelaySec(x) ) is that it doesn’t give me a means to abort the delay if the expected serial operation completes successfully.

Seems like everywhere I turn, I get stopped. I thought maybe I could abort in a firsttime block if time had run out but DC won’t let you do that…

Thanks for chiming in though!

[QUOTE=rabideric;4490]What I’m trying to do is figure out a way to timeout on a serial operation. The problem with waitfor( DelaySec(x) ) is that it doesn’t give me a means to abort the delay if the expected serial operation completes successfully.

Seems like everywhere I turn, I get stopped. I thought maybe I could abort in a firsttime block if time had run out but DC won’t let you do that…

Thanks for chiming in though![/quote]

Ahh!! that’s where I was a few weeks ago. I solved that issue with costates and making a separate listener co_state that just waited for a response and a separate data processor for the received bytes.

costate talker_C sends out a message and waits 5 seconds for a receivebuffersz to be filled with 5 characters. After 5 seconds no matter what, it continues its processing.

costate listener_C is just sitting there waiting indefinitely for a string of characters. when it gets them it updates the receivebuffersz

This is far from perfect but it’s a starting point.

Here’s the code and it runs on a BL2600 using dynamic C 9.xx:

/********************************************************************
write_uart01.c
ok lets try to rewrite simple3wire to make it a bit more rohbust.

was:
Simple3wire.c
Z-World 2004

This program is used with BL2600 series controllers.

Description
===========
This program demonstrates basic initialization for a
simple RS232 3-wire loopback displayed in STDIO window.

Here’s the typical connections for a 3 wire interface that
would be made between controllers.

   TX <---> RX
   RX <---> TX
	Gnd <---> Gnd

Connections
===========
1. Connect TXC to RXF located on J17.
  1. Connect TXF to RXC located on J17.
Instructions
============
1. Compile and run this program.
2. View STDIO window for sample program results.

090922 JL first run works!
090922 JL let’s get it so it doesn’t lock up, use waitfor rather than
while wait forever. - well that’s no good, lets revert it back
and we are going to have to go with something completely different.
090923 JL OK talker and listener are working, you send out a message, and wait
5 seconds for a response. If 2 responses come within the 5 seconds,
you destroy the first response. May or may not be what you want, but
basically this demonstrates a send and ack with timeout for no response.

********************************************************************/
// Set a default of declaring all local variables “auto” (on stack)
#class auto

// serial buffer size
#define CINBUFSIZE 15
#define COUTBUFSIZE 15
#define FINBUFSIZE 15
#define FOUTBUFSIZE 15

// serial baud rate
#define BAUD232 115200

main()
{
auto int nIn;
auto char cOut;
char outbuffersz[44];
char inbuffersz[44];
char receivebuffersz[44];
char msg01sz[17];
int nread;
auto int tempint;
int loopcounter;

outbuffersz[0] = 0;
inbuffersz[0] = 0;
receivebuffersz[0] = 0;
strcpy(msg01sz, ":hello world!!!
");
loopcounter = 0;

// Initialize controller
brdInit();

// Open serial port
serCopen(BAUD232);
serFopen(BAUD232);

// Can use serial mode 0 - 2 for 3 wire RS232 operation
// depending on what serial port you select and serMode
// must be executed after the serXopen function(s).
serMode(0);

// Clear serial buffers
serCwrFlush();
serCrdFlush();
serFwrFlush();
serFrdFlush();

printf (“write uart test 090922_01 strlen outbuffersz=%d
“,strlen(outbuffersz));
loopinit();
while (1) {
loophead();
costate talker_C always_on {
loopcounter++;
sprintf(outbuffersz,”%05d%s”,loopcounter,msg01sz);
wfd { cof_serCwrite(outbuffersz, strlen(outbuffersz)); }
waitfor (DelaySec(5));
if (strlen(receivebuffersz) > 0) {
printf("talker_c:%05d: received answer >%s<
"
,loopcounter,receivebuffersz);
receivebuffersz[0] = 0;
} //then
else {
printf(“talker_c:%05d: no response, abort
"
,loopcounter);
}//else
}//costate talker_C
costate listener_C always_on {
wfd nread = cof_serCread(inbuffersz, 5, 2000); //2 seconds max between characters
inbuffersz[nread] = 0;
printf (” listener_C:got %d chars: >%s<
",
strlen(inbuffersz),
inbuffersz);
strcpy(receivebuffersz,inbuffersz);
}//costate listener_C
}//while (1) main loop

}//main

why can’t forums respect whitespace??? geez…

I’ll attach the source…

Well here’s the rub. cof_serXread will never timeout unless at least one character has been read. Why did they do that?

I’m writing RS485 command-response code that first, has to collect the received data that is read back from the command. The rub is that if the 485 bus is shorted, there will not be one character echoed back so cof_serXread will never abort. Of course, I can manually wait for the first character but cripes…

I agree on the whitespace. I can’t imagine those few extra bytes per post are costing Rabbit much.

The above gripes notwithstanding. I think you have a good start. Since I’m in command response mode, I’ll have to be careful separating the transmit and receive code.

All of this could be avoided if I could just check the time in a high level firsttime block and abort right there.

[QUOTE=rabideric;4493]Well here’s the rub. cof_serXread will never timeout unless at least one character has been read. Why did they do that?

I’m writing RS485 command-response code that first, has to collect the received data that is read back from the command. The rub is that if the 485 bus is shorted, there will not be one character echoed back so cof_serXread will never abort. Of course, I can manually wait for the first character but cripes…

I agree on the whitespace. I can’t imagine those few extra bytes per post are costing Rabbit much.

The above gripes notwithstanding. I think you have a good start. Since I’m in command response mode, I’ll have to be careful separating the transmit and receive code.

All of this could be avoided if I could just check the time in a high level firsttime block and abort right there.[/quote]

“Of course, I can manually wait for the first character but cripes…”
Not sure what you mean by this. My problem more times than not is that the cable is disconnected or the other device isn’t powered up (isn’t that 99.9999% of the problem with comm’s??) so I will never get ANY characters. So the process just hangs forever… Not a good design. My solution means that just that costate (listener_C) hangs. That works fine in my RS232 world, but I’m pretty sure that will make a mess of your RS485 world. I can’t imagine the rabbit will take kindly to you changing from RX to TX while an cof_serXread is still outstanding. Give it a try. You also might want to try using the RS232 port and then use an external RS232-RS485 converter. Then my program will work for you.

Yeah I’ve been fighting with that infinite wait too. You’re not alone. Meantime my other thread shows another issue; when you are using 2 ports…

I’m using the the RS485 port for an outgoing messages 4x20 lcd display so I have avoided the state change issues associated with the RS485.

I can’t imagine why the cof_serXread doesn’t have TWO timeout settings, one for between characters (aka the one they already have) and a second to make sure the routine always returns and never hangs.

“All of this could be avoided if I could just check the time in a high level firsttime block and abort right there”

I think you might be able to abort the read using the abandon statement,
and there is a struct associate with the cofunction and I think it has some time values in there that can be checked.
you might want to check that out. I’d be interested in how you get a “timeout” of a ser_xread when you have no connection/ no first character.

I have no idea what you mean by “firsttime block”

I could do something like this:

[ol]
[li]Switch tx on
[/li][li]send a batch of command characters
[/li][li]delay 1.5 character times
[/li][li]check serCrdUsed and bomb out if no chars are present.
[/li][li]do a cof_serCread to collect “echo” of all but last character. // you’ll see why
[/li][li]spin in while with serCrdUsed again waiting for last char with timeout via MS_TIMER
[/li][li]When last char is in buffer, read it and switch Tx off immediately // This is why
[/li][li]Use serCrdUsed again with MS_TIMER timeout to wait for first response char in buffer
[/li][li]Use cof_serCread to read entire response with timeout.
[/li]
I think that covers all possibilities of no echo, no response, trashed response, etc. It also handles the immediate switch from Tx to Rx which I need because my remote units respond within on character time. There can’t be any task switching going on right then.

A firsttime block is a code block that runs every time a cofunction is “continued”. Curiously enough, it is not run the first time a function is invoked. Suspect the keyword “continuation” might have been a better choice. Still might be useful in this process for running a global timeout.
[/ol]

[QUOTE=rabideric;4497]I could do something like this:

[ol]
[li]Switch tx on
[/li][li]send a batch of command characters
[/li][li]delay 1.5 character times
[/li][li]check serCrdUsed and bomb out if no chars are present.
[/li][li]do a cof_serCread to collect “echo” of all but last character. // you’ll see why
[/li][li]spin in while with serCrdUsed again waiting for last char with timeout via MS_TIMER
[/li][li]When last char is in buffer, read it and switch Tx off immediately // This is why
[/li][li]Use serCrdUsed again with MS_TIMER timeout to wait for first response char in buffer
[/li][li]Use cof_serCread to read entire response with timeout.
[/li]
I think that covers all possibilities of no echo, no response, trashed response, etc. It also handles the immediate switch from Tx to Rx which I need because my remote units respond within on character time. There can’t be any task switching going on right then.

A firsttime block is a code block that runs every time a cofunction is “continued”. Curiously enough, it is not run the first time a function is invoked. Suspect the keyword “continuation” might have been a better choice. Still might be useful in this process for running a global timeout.
[/ol][/quote]

WAIT A MINUTE!!! serXrdUsed() ??? where did that one come from? I didn’t know a function like that exsisted. That looks like it should be a perfect solution:

costate Listener_E always on {

if (waitingformessage) {
-------if (serErdUsed() > 0) {
----------------- wfd nread = cof_serEread(inbuffersz, 999, 150);
-----------------noinputtickcount = 0;
-----------------waitingformessage = 0;
---------------- }
----------- else {
-----------------noinputtickcount++;
-----------------waitfor(DelayMs(30));
-----------------}
-------}//then waitingformessage true
–else {
--------waitfor(waitingformessage);
-------}//else

}//costate Listener_E

costate waitingonEmessage always_on {

// do something to wake up E device goes here
waitingformessage = 1;

waitfor (delayMs(???));

if (noinputtickcount > ???) {
------ no response code goes here which includes:
--------waitingformessage = 0;
--------noinputtickcount = 0;
-------}//then we timed out
–else {
-------if (nread >0) {
------------- read has been satisfied, process read here
--------------}//then we have response from E device.
-------}//else timer not yet expired

}//waitingonEmessage

The idea is you never do a cof_serXread on an empty buffer. Shouldn’t that work? Why bother with all the leave one character behind stuff?

never serXread’ing on an empty buffer should work.

I still have to have that that funny thing with the last character because my remote 485 devices start responding within one character time of the last character of the transmitted command. I have to wait until that last character is fully transmitted then immediately switch off the transmitter so the remote can drive the bus. Remember that with 485 both the Rabbit and the remote talk on the same wire pair so the rabbit must disable the transmitter or it won’t see the remote’s response.

I learned the hard way, that I cannot allow any task switch during this time because control might not return in time to shut off the transmitter.

[QUOTE=rabideric;4500]never serXread’ing on an empty buffer should work.

I still have to have that that funny thing with the last character because my remote 485 devices start responding within one character time of the last character of the transmitted command. I have to wait until that last character is fully transmitted then immediately switch off the transmitter so the remote can drive the bus. Remember that with 485 both the Rabbit and the remote talk on the same wire pair so the rabbit must disable the transmitter or it won’t see the remote’s response.

I learned the hard way, that I cannot allow any task switch during this time because control might not return in time to shut off the transmitter.[/quote]

I realize that about RS485, but it seems to me that if you always keep the RS485 device in TX mode by default, and then switch it to RX if and only if waitingformessage is true, then you are all set. Basically if you set waitingformessage to 0, you set the RS485 to TX, if you set waitingformessage to 1 you set the RS485 to RX (aka, if you are “waiting for a message” you shouldn’t be talking)

Yes that is an annoying thing about RS485. In my app I have 3 RS232 devices but one of them is read only, it never sends a response, so I use the RS485 port on that device. All I have to do is set it to TX send my messages and forget about it. After the rabbit I have a RS485-RS232 converter in line.

In your situation, I would still consider using the RS232 port and use an inline RS232-RS422 converter. Saves a whole lot of traffic cop programming.