Windows版 iTRONサービスコールの作成 (メッセージバッファ)
(その5)
メッセージバッファからメッセージを受信するサービスコールは以下のとおりです。
サービスコール名 | 説明 |
---|---|
rcv_mbf | タイムアウトなしメッセージバッファからメッセージを受信します。 |
prcv_msg | ポーリング方式でメッセージバッファからメッセージを受信します。 |
trcv_msg | タイムアウト付きでメッセージバッファからメッセージを受信します。 |
※ Ver3.0と4.0では関数名は同じですが、引数が異なりますので注意してください。
メッセージバッファからメッセージを受信する (タイムアウトなし)
タイムアウトなしでメッセージバッファからメッセージを受信するには以下のサービスコールを使用します。
・Ver3.0の場合
ER rcv_mbf( VP msg, INT *p_msgsz, ID mbfid )
引数 | 説明 |
---|---|
msg | 受信したメッセージを格納する領域のポインタ。 |
p_msgsz | 受信したメッセージのバイト数を格納する領域のポインタ。 |
mbfid | メッセージバッファID番号。 |
戻り値 | 説明 |
---|---|
E_OK | 正常終了。 |
E_ID | 範囲外のメッセージバッファID番号。 |
E_NOEXS | 指定したメッセージバッファID番号は登録されていない。 |
E_PAR | パラメータエラー。 |
E_CTX | 非コンテキスト・タスクからの呼び出し。 |
・Ver4.0の場合
ER_UINT rcv_mbf( ID mbfid, VP msg )
引数 | 説明 |
---|---|
mbfid | メッセージバッファID番号。 |
msg | 受信したメッセージを格納する領域のポインタ。 |
戻り値 | 説明 |
---|---|
E_ID | 範囲外のメッセージバッファID番号。 |
E_NOEXS | 指定したメッセージバッファID番号は登録されていない。 |
E_PAR | パラメータエラー。 |
E_CTX | 非コンテキスト・タスクからの呼び出し。 |
その他の正数 | 受信したメッセージのバイト数。 |
・Ver3.0のサービスコールのソースコードは以下のようになります。
/****************************************************************************/
/*!
* @brief メッセージバッファからの受信 (タイムアウトなし).
*
* @param [out] msg メッセージを格納する領域のポインタ.
* @param [out] msg メッセージサイズを格納する領域のポインタ.
* @param [in] mbfid メッセージバッファID番号.
*
* @retval エラーコード.
*/
ER rcv_mbf( VP msg, INT *p_msgsz, ID mbfid )
{
//! タイムアウトなしでメッセージバッファからメッセージを受信する.
return trcv_mbf( msg, p_msgsz, mbfid, TMO_FEVR );
}
・Ver4.0のサービスコールのソースコードは以下のようになります。
/****************************************************************************/
/*!
* @brief メッセージバッファからの受信 (タイムアウトなし).
*
* @param [in] mbfid メッセージバッファID番号.
* @param [out] msg メッセージを格納する領域のポインタ.
*
* @retval メッセージサイズまたはエラーコード.
*/
ER_UINT rcv_mbf( ID mbfid, VP msg )
{
//! タイムアウトなしでメッセージバッファからメッセージを受信する.
return trcv_mbf( mbfid, msg, TMO_FEVR );
}
trcv_mbf()のタイムアウト設定に TMO_FEVR を指定して呼び出します。
メッセージバッファにメッセージが無い場合は、メッセージバッファにメッセージが格納されるまでタスクをスリープさせます。
ポーリングでメッセージバッファからメッセージを受信するには以下のサービスコールを使用します。
・Ver3.0の場合
ER prcv_mbf( VP msg, INT *p_msgsz, ID mbfid )
引数 | 説明 |
---|---|
msg | 受信したメッセージを格納する領域のポインタ。 |
p_msgsz | 受信したメッセージのバイト数を格納する領域のポインタ。 |
mbfid | メッセージバッファID番号。 |
戻り値 | 説明 |
---|---|
E_OK | 正常終了。 |
E_ID | 範囲外のメッセージバッファID番号。 |
E_NOEXS | 指定したメッセージバッファID番号は登録されていない。 |
E_PAR | パラメータエラー。 |
E_CTX | 非コンテキスト・タスクからの呼び出し。 |
E_TMOUT | メールボックスにメッセージがない。 |
・Ver4.0の場合
ER_UINT prcv_mbf( ID mbfid, VP msg )
引数 | 説明 |
---|---|
mbfid | メッセージバッファID番号。 |
msg | 受信したメッセージを格納する領域のポインタ。 |
戻り値 | 説明 |
---|---|
E_ID | 範囲外のメッセージバッファID番号。 |
E_NOEXS | 指定したメッセージバッファID番号は登録されていない。 |
E_PAR | パラメータエラー。 |
E_CTX | 非コンテキスト・タスクからの呼び出し。 |
E_TMOUT | メールボックスにメッセージがない。 |
その他の正数 | 受信したメッセージのバイト数。 |
・Ver3.0のサービスコールのソースコードは以下のようになります。
/****************************************************************************/
/*!
* @brief メッセージバッファからの受信 (ポーリング).
*
* @param [out] msg メッセージを格納する領域のポインタ.
* @param [out] msg メッセージサイズを格納する領域のポインタ.
* @param [in] mbfid メッセージバッファID番号.
*
* @retval エラーコード.
*/
ER prcv_mbf( VP msg, INT *p_msgsz, ID mbfid )
{
//! 受信待ちなしでメッセージバッファからメッセージを受信する.
return trcv_mbf( msg, p_msgsz, mbfid, TMO_POL );
}
・Ver4.0のサービスコールのソースコードは以下のようになります。
/****************************************************************************/
/*!
* @brief メッセージバッファからの受信 (ポーリング).
*
* @param [in] mbfid メッセージバッファID番号.
* @param [out] msg メッセージを格納する領域のポインタ.
*
* @retval メッセージサイズまたはエラーコード.
*/
ER_UINT prcv_mbf( ID mbfid, VP msg )
{
//! 受信待ちなしでメッセージバッファからメッセージを受信する.
return trcv_mbf( mbfid, msg, TMO_POL );
}
trcv_mbf()のタイムアウト設定に TMO_POL を指定して呼び出します。
メッセージバッファにメッセージが無い場合は、タスクをスリープさせずに戻り値をE_TMOUTとしてすぐにサービスコールから戻ります。
メッセージバッファからメッセージを受信する (タイムアウト付き)
タイムアウト付きでメッセージバッファからメッセージを受信するには以下のサービスコールを使用します。
・Ver3.0の場合
ER trcv_mbf( VP msg, INT *p_msgsz, ID mbfid, TMO tmout )
引数 | 説明 |
---|---|
msg | 受信したメッセージを格納する領域のポインタ。 |
p_msgsz | 受信したメッセージのバイト数を格納する領域のポインタ。 |
mbfid | メッセージバッファID番号。 |
tmout |
タイムアウトの設定値。
|
戻り値 | 説明 |
---|---|
E_OK | 正常終了。 |
E_ID | 範囲外のメッセージバッファID番号。 |
E_NOEXS | 指定したメッセージバッファID番号は登録されていない。 |
E_PAR | パラメータエラー。 |
E_CTX | 非コンテキスト・タスクからの呼び出し。 |
E_TMOUT | メッセージを受信できずにタイムアウト。 |
・Ver4.0の場合
ER_UINT trcv_mbf( ID mbfid, VP msg, TMO tmout )
引数 | 説明 |
---|---|
mbfid | メッセージバッファID番号。 |
msg | 受信したメッセージを格納する領域のポインタ。 |
tmout |
タイムアウトの設定値。
|
戻り値 | 説明 |
---|---|
E_ID | 範囲外のメッセージバッファID番号。 |
E_NOEXS | 指定したメッセージバッファID番号は登録されていない。 |
E_PAR | パラメータエラー。 |
E_CTX | 非コンテキスト・タスクからの呼び出し。 |
E_TMOUT | メッセージを受信できずにタイムアウト。 |
その他の正数 | 受信したメッセージのバイト数。 |
・Ver3.0のサービスコールのソースコードは以下のようになります。
/****************************************************************************/
/*!
* @brief メッセージバッファからの受信 (タイムアウトあり).
*
* @param [out] msg メッセージを格納する領域のポインタ.
* @param [out] msg メッセージサイズを格納する領域のポインタ.
* @param [in] mbfid メッセージバッファID番号.
* @param [in] tmout タイムアウト設定.
*
* @retval エラーコード.
*/
ER trcv_mbf( VP msg, INT *p_msgsz, ID mbfid, TMO tmout )
{
ER ercd;
wi_CommonLock();
//! メッセージバッファからメッセージを受信する.
ercd = wi_ReceiveMsgBuffer( mbfid, msg, p_msgsz, tmout );
wi_CommonUnlock();
return ercd;
}
・Ver4.0のサービスコールのソースコードは以下のようになります。
/****************************************************************************/
/*!
* @brief メッセージバッファからの受信 (タイムアウトあり).
*
* @param [in] mbfid メッセージバッファID番号.
* @param [out] msg メッセージを格納する領域のポインタ.
* @param [in] tmout タイムアウト設定.
*
* @retval メッセージサイズまたはエラーコード.
*/
ER_UINT trcv_mbf( ID mbfid, VP msg, TMO tmout )
{
ER ercd;
INT size;
wi_CommonLock();
//! メッセージバッファからメッセージを受信する.
ercd = wi_ReceiveMsgBuffer( mbfid, msg, &size, tmout );
if( ercd == E_OK ){
ercd = size;
}
wi_CommonUnlock();
return ercd;
}
メッセージバッファからメッセージを取得する関数を呼び出してメッセージを取得します。
メッセージバッファにメッセージが無い場合は、メッセージを受信するか、タイムアウトで設定した時間が経過するまでタスクをスリープさせます。
メッセージバッファからメッセージを取得する関数
メッセージバッファからメッセージを取得する関数のソースコードは以下のとおりです。
/****************************************************************************/
/*!
* @brief メッセージバッファからの受信.
*
* @param [in] id メッセージバッファID番号.
* @param [out] msg メッセージを格納する領域のポインタ.
* @param [out] size メッセージ・サイズを格納する領域のポインタ.
* @param [in] tmout タイムアウト設定.
*
* @retval エラーコード.
*/
ER wi_ReceiveMsgBuffer( INT id, VP msg, INT *size, TMO tmout )
{
ER ercd;
WIMBFOBJ *p;
WITSKOBJ *tsk;
//! メッセージバッファIDのオブジェクトを取得する.
p = (WIMBFOBJ *)wi_FindObject( id, TMAX_MAXMBF, ObjList, &ercd );
if( !p ){
return ercd;
}
//! 引数が不正な場合はエラーにする.
if( !msg || !size ){
return E_PAR;
}
//! メッセージバッファからメッセージを取り出す.
*size = GetMessageData( p, (BYTE *)msg );
if( *size > 0 ){
//! メッセージバッファが空いたので送信待ちをしているメッセージをバッファに書き込む.
RestoreMessageData( p );
return E_OK;
}
/*!
* メッセージ送信待ちをしているタスクがある場合、
* メッセージ送信タスクから直接メッセージを受け取る.
*/
if( p->SndWaitQue ){
//! メッセージ送信待ちをしているタスクのコンテキストを取り出す.
tsk = (WITSKOBJ *)(p->SndWaitQue);
if( tsk->Param[0] ){
//! メッセージ送信タスクから直接メッセージをコピーする.
memcpy( msg, tsk->Param[0], (INT)(tsk->Param[1]) );
tsk->Param[0] = NULL;
tsk->Param[1] = 0;
/*!
* メッセージバッファに書き込めるかもしれないので
* 送信待ちをしているメッセージをバッファに書き込む.
*/
RestoreMessageData( p );
return E_OK;
}else{
/*!
* 送信メッセージが無いのに送信待ちをしているタスクがあった場合、
* 送信待ちをしているタスクは起床させる.
*/
wi_TaskWakeup( tsk->Hdr.Id, TTW_SMBF, &(p->SndWaitQue) );
return E_SYS;
}
}
//! 自タスクのタスクコンテキストにメッセージ格納バッファのポインタをセットする.
tsk = wi_GetTaskObject( TSK_SELF );
if( !tsk ){
return E_CTX;
}
tsk->Param[0] = msg;
tsk->Param[1] = 0;
//! メッセージを受信するまでタスクをスリープさせる.
ercd = wi_TaskWait( id, TTW_RMBF, tmout, p->RcvAttr, &(p->RcvWaitQue) );
if( ercd == E_OK ){
*size = (INT)tsk->Param[1];
}
return ercd;
}
メッセージバッファからメッセージを取得する関数は以下のような処理を行います。
- 引数で指定されたメッセージバッファID番号に該当するメッセージバッファ・オブジェクトを取り出します。
- メッセージバッファにメッセージが格納されていれば、先頭のメッセージをコピーして終了します。
- メッセージ送信待ちをしているタスクがある場合、メッセージ送信待ち行列の先頭のタスクのメッセージを受け取って終了します。
- メッセージ送信待ちをしているタスクがない場合、メッセージを受信するか、タイムアウトになるまでタスクをスリープさせます。
- タスクのスリープから復帰したときに正常終了で復帰した場合のみ、受信したメッセージのバイト数をメッセージ・サイズを格納する領域(size)にセットします。
/****************************************************************************/
/*!
* @brief メッセージバッファからメッセージを取り出す.
*
* @param [in] p メッセージバッファ・オブジェクトのポインタ.
* @param [out] msg メッセージを格納する領域のポインタ.
*
* @retval 取り出したメッセージのサイズ.
*/
static INT GetMessageData( WIMBFOBJ *p, BYTE* msg )
{
int i;
DWORD len;
//! メッセージバッファにデータがない場合は処理を終了する.
if( !p->MsgBuf || p->GetPos == p->PutPos ){
return 0;
}
/*!
* メッセージ・サイズの途中でリングバッファを折り返したかもしれないので
* メッセージ・サイズを1バイトづつバッファから取り出す.
*/
len = 0;
for( i = 0; i < 4; i++ ){
//! メッセージのサイズを1バイトづつ取り出す.
len = (len << 8) + ((DWORD)p->MsgBuf[p->GetPos] & 0x000000FF);
//! バッファの読み出し位置を更新する.
p->GetPos = wi_IncRingPositon( p->GetPos, p->MsgSize );
}
//! メッセージがリングバッファを折り返す場合は2回に分けてメッセージを取り出す.
if( (p->GetPos + len) > p->MsgSize ){
DWORD size1,size2;
//! リングバッファの最後からメッセージの先頭データをコピーする.
size1 = p->MsgSize - p->GetPos;
memcpy( msg, &p->MsgBuf[p->GetPos], size1 );
//! リングバッファの先頭から残りのメッセージをコピーする.
size2 = len - size1;
memcpy( &msg[size1], p->MsgBuf, size2 );
//! バッファの読み出し位置を更新する.
p->GetPos = size2;
}else{
//! バッファからメッセージをコピーする.
memcpy( msg, &p->MsgBuf[p->GetPos], len );
//! バッファの読み出し位置を更新する.
p->GetPos += len;
}
return (INT)len;
}
メッセージバッファからメッセージを取り出すには、最初に「メッセージのバイト数」を取り出し、次に「メッセージデータ」を取り出します。
メッセージバッファはリングバッファにしてありますので、バッファの領域外を参照しないように「メッセージのバイト数」は1バイトづつ読み込みます。
「メッセージデータ」の方はデータの読み出し位置から判定して、2回に分けてバッファの領域外を参照しないように読み込みます。
/****************************************************************************/
/*!
* @brief メッセージ送信待ちをしているデータをバッファに書き込む.
*
* @param [in] p メッセージバッファ・オブジェクトのポインタ.
*
* @retval なし.
*/
static void RestoreMessageData( WIMBFOBJ *p )
{
WITSKOBJ *tsk;
tsk = (WITSKOBJ *)(p->SndWaitQue);
while( tsk ){
//! メッセージがバッファに書き込めない場合は処理を終了にする.
if( tsk->Param[0] ){
if( !PutMesageData( p, (BYTE *)(tsk->Param[0]), (INT)(tsk->Param[1]) ) ){
break;
}
}
//! メッセージの送信を待っているタスクを起床させる.
wi_TaskWakeup( tsk->Hdr.Id, TTW_SMBF, NULL );
//! 待ち行列から次のタスクを取り出す.
tsk = tsk->QueLink;
}
}
メッセージバッファからメッセージ受信処理によりメッセージが取り出されたときに、メッセージが取り出されたことにより空いた領域に、メッセージの送信待ちをしているメッセージデータを新たに格納します。
メッセージバッファに送信メッセージデータを書き込めたら、送信完了待ちをしているタスクを起床します。
これをメッセージバッファに送信メッセージデータが格納できなくなるか、送信待ちをしているタスクがなくなるまで繰り返します。