Author Topic: "Stack up" messages to tasks  (Read 4779 times)

Offline ZeroOne

  • Newbie
  • *
  • Posts: 12
    • View Profile
"Stack up" messages to tasks
« on: March 24, 2008, 10:23:07 AM »
Hi

Is it possible to "stack up" messages to a task, for them to be read later on?

For instance:

funktion1 is set run every 10 sek. funktion2 is running all the time (UTASKER_GO) sending internal messages to funktion1.

Is it possible to read all the messages send by funktion2 every 10 sek? or will funktion1 respond everytime a message arrives?

ZeroOne

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3076
    • View Profile
    • uTasker
Re: "Stack up" messages to tasks
« Reply #1 on: March 24, 2008, 01:14:15 PM »
ZeroOne

Sending a message to another task always wakes it, so queueing would need a trick somewhere, but there are various possibilities.

1. Don't send it from function2 via a queue but rather store it to a global buffer. When function1 starts every 10s it can collect all messages from the global buffer.

2. Send each message normally. This will wake function1 each time and it can read the input and store it to a local buffer. Only when the 10s timer is recognised, it can treat the local buffer.

3. Create a dummy task which has an input queue large enough to receive all messages and send them from function2 to this dummy task (function1). The dummy task does nothing so it never clears this queue itself, but it does make a copy if its internal queue ID.
When function1 starts after 10s it can read the dummy task's queue rather than its own. If it is periodically being scheduled it doesn't even need a queue of its own.

This is quite a nice solution since the two task can be put into one file and only the input queue handle needs to be shared. Here's an example:

/**************************************************************************/
static QUEUE_HANDLE PortIDInternalQueue;                                          // queue ID for task input
void fnDummyQueueTask(TTASKTABLE *ptrTaskTable)
{
    PortIDInternalQueue= ptrTaskTable->TaskID;                                    // remember the input handle but don't read the queue
}

void fnMainTask(TTASKTABLE *ptrTaskTable)                                        // scheduled once every 10s
{
    while ( fnRead( PortIDInternalQueue, ucInputMessage, HEADER_LENGTH )) { // read the queued inputs every 10s
        .....
    }
}
/**************************************************************************/


Just remember that the input queue to the queueing task needs to be large enough to hold all messages received within the 10s interval!!
In taskconfig.h (eg. with 8k queue)

  { "QueueTask", fnDummyQueueTask,      (8*1024),      0, 0, UTASKER_STOP},         // queue task



There are probably lots of other techniques too but I think that the dummy queue task is worth trying.

Regards

Mark

Offline ZeroOne

  • Newbie
  • *
  • Posts: 12
    • View Profile
Re: "Stack up" messages to tasks
« Reply #2 on: March 26, 2008, 03:27:00 PM »
Hi Mark

Tried "Dummy task idea" above and it seems to work partially. I am only able to write out one message (the first one). The rest of the messages don't seem to be stored. The fnRead() in the while-loop is not reading anything (returns 0) when it's supposed to read the sekund message.
I have the following code to read the content of the queue:

Code: [Select]

//include
#include "config.h"

//Global variables
static QUEUE_HANDLE PortIDInternalQueue;                                          // queue ID for task input

//Prototypes
void fnDummyQueueTask(TTASKTABLE *ptrTaskTable);

//functions
void fnRxMessage(TTASKTABLE *ptrTaskTable)                                        // scheduled once every 2s
{
unsigned char ucRxMessage[SMALL_MESSAGE] = {0};
static QUEUE_HANDLE SerialID;
int len = 0;

    while ( fnRead( PortIDInternalQueue, ucRxMessage, HEADER_LENGTH + 1 ))  // read the queued inputs every 2s.
{
SerialID = ucRxMessage[MSG_CONTENT_COMMAND];

switch(ucRxMessage[MSG_SOURCE_TASK]) // Source of message
{
case TASK_TX_MESSAGE:
{
fnRead( PortIDInternalQueue, ucRxMessage, SMALL_MESSAGE);
len = uStrlen(ucRxMessage);

fnWrite(SerialID, ucRxMessage, len);
}
}
    }
}

void fnDummyQueueTask(TTASKTABLE *ptrTaskTable)
{
    PortIDInternalQueue = ptrTaskTable->TaskID;                                    // remember the input handle but don't read the queue
}

The following code is used to build and send the message to the fnDummyQueueTask() and to set up the UART. :

Code: [Select]
//Includes
#include "config.h"

//defines


//Global varables

//Prototypes
void fnSendToDummy(unsigned char ucCommand, unsigned char *ucData, unsigned char ucLength);
QUEUE_HANDLE fnConfigureUART(unsigned char mode);

//functions
void fnTxMessage(TTASKTABLE *ptrTaskTable)
{
static unsigned int uiState;
unsigned char ucRxUART[SMALL_MESSAGE] = {0};

static QUEUE_HANDLE SerialID;

if(uiState == 0)
{
SerialID = fnConfigureUART(FOR_I_O);
uTaskerStateChange(TASK_TX_MESSAGE, UTASKER_STOP);
uiState = 1;
}


if(fnRead(SerialID, (unsigned char *)ucRxUART, SMALL_MESSAGE) != 0)
fnSendToDummy(SerialID, (unsigned char *)ucRxUART, uStrlen(ucRxUART));
else
fnSendToDummy(SerialID, (unsigned char *)"empty\r", uStrlen("empty\r"));
}

void fnSendToDummy(unsigned char ucCommand, unsigned char *ucData, unsigned char ucLength)
{
unsigned char ucTxMessage[ HEADER_LENGTH + SMALL_MESSAGE + 1];

ucTxMessage[ MSG_DESTINATION_NODE ]  = INTERNAL_ROUTE;
ucTxMessage[ MSG_SOURCE_NODE ]       = INTERNAL_ROUTE;
ucTxMessage[ MSG_DESTINATION_TASK ]  = TASK_DUMMY;
ucTxMessage[ MSG_SOURCE_TASK ]       = TASK_TX_MESSAGE;            
ucTxMessage[ MSG_CONTENT_LENGTH ]    = ucLength;
ucTxMessage[ MSG_CONTENT_COMMAND ]   = ucCommand;

if (fnWrite(INTERNAL_ROUTE, ucTxMessage, 0))
fnWrite(INTERNAL_ROUTE, ucData, ucLength);

}

QUEUE_HANDLE fnConfigureUART(unsigned char mode)
{
TTYTABLE tInterfaceParameters;       //table for passing information to driver
QUEUE_HANDLE SerialPortID;

tInterfaceParameters.Channel = 0;  // serial 0, 1, 2, 3, 4, etc.
  tInterfaceParameters.ucSpeed =  SERIAL_BAUD_19200; // baud rate
  tInterfaceParameters.Rx_tx_sizes.RxQueueSize = RX_BUFFER_SIZE;       // input buffer size
tInterfaceParameters.Rx_tx_sizes.TxQueueSize = TX_BUFFER_SIZE;
    tInterfaceParameters.Task_to_wake = TASK_TX_MESSAGE;
tInterfaceParameters.usConfig = (CHAR_8| NO_PARITY | ONE_STOP | NO_HANDSHAKE | MSG_MODE);    
tInterfaceParameters.ucMessageTerminator = '\r';

if (SerialPortID = fnOpen( TYPE_TTY, mode, &tInterfaceParameters ))  // open or change the channel with defined configurations (initially inactive)
        fnDriver( SerialPortID, (RX_ON | TX_ON ), 0 );        // enable RX

return SerialPortID;
 
}

In TaskConfig.h I have the following configuration:

Code: [Select]
{ "1_Tx", fnTxMessage, NO_QUE, 0, 0, UTASKER_GO},
  { "2_Rx", fnRxMessage, NO_QUE, 0, (DELAY_LIMIT)(10 * SEC), UTASKER_STOP},
  { "3_Dummy", fnDummyQueueTask, (2 * 1024), 0, 0, UTASKER_STOP},

I am fully able to send and recieve from the UART using message mode. During the 10 sec I can send messages from the UART and everytime the fnTxMessage() is called so the messages can be build, sent and stored. When the 10 sec are done only the first message sent is recieved the rest is gone.

Can't figure out what is wrong

ZeroOne

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3076
    • View Profile
    • uTasker
Re: "Stack up" messages to tasks
« Reply #3 on: March 26, 2008, 05:38:23 PM »
Hi

I think that the following may work:

    len = fnRead( PortIDInternalQueue, ucRxMessage, ucRxMessage[MSG_CONTENT_LENGTH]);            
    fnWrite(SerialID, ucRxMessage, len);


In your method:
    fnRead( PortIDInternalQueue, ucRxMessage, SMALL_MESSAGE);
will read all messages in the input queue into ucRxMessage.
    len = uStrlen(ucRxMessage); will then probably give the length of the first string in the queue (although there are proabably several strings waiting there)

The method which I have suggested will only read the contents of the first waiting message (its length is already known so uStrlen() can be saved) and so the while loop in the task will then read out each individual message and do the same for each.

Do you test this with the simulator? If you check the value returned by fnRead() in your case you will probably see that it is the length of all waiting messages in the queue (including each individual message header).

I note that you are using the DOUBLE QUEUE WRITE method to send the internal message - this is good because it is more efficent when sending data. Beware however that the maximum data length is 254 bytes! You can also optimise the transmitting routine by reducing the buffer to
unsigned char ucTxMessage[ HEADER_LENGTH + 1]; - this is adequate
Ensure that #define SUPPORT_DOUBLE_QUEUE_WRITES is defined in config.h.

Last point: Ensure that this task dosn't receive timer or event messages because reading (HEADER_LENGTH + 1) could in this case cause queue corruption. It is of course more efficient in your case since all of your messages are of this format, but should be used carefully...

The details of using this is also on page 14/24 of the OS basics document at http://www.utasker.com/docs/uTasker/uTaskerV1.3.PDF

Good luck.

Regards

Mark


Offline ZeroOne

  • Newbie
  • *
  • Posts: 12
    • View Profile
Re: "Stack up" messages to tasks
« Reply #4 on: March 26, 2008, 11:05:52 PM »
Hi again

Quote
I think that the following may work:

    len = fnRead( PortIDInternalQueue, ucRxMessage, ucRxMessage[MSG_CONTENT_LENGTH]);           
    fnWrite(SerialID, ucRxMessage, len);

In your method:
    fnRead( PortIDInternalQueue, ucRxMessage, SMALL_MESSAGE);
will read all messages in the input queue into ucRxMessage.
    len = uStrlen(ucRxMessage); will then probably give the length of the first string in the queue (although there are proabably several strings waiting there)

That did the trick

Thank you

ZeroOne