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

タイムアウトの設定値。

  • TMO_FEVRの場合、永久にメッセージが受信できるのを待ちます。
  • TMO_POLの場合、メッセージが受信できなくてもタスクをスリープしません。
  • その他の正数はタイムアウト値となります。

戻り値説明
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

タイムアウトの設定値。

  • TMO_FEVRの場合、永久にメッセージが受信できるのを待ちます。
  • TMO_POLの場合、メッセージが受信できなくてもタスクをスリープしません。
  • その他の正数はタイムアウト値となります。

戻り値説明
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;
    }
}
	

メッセージバッファからメッセージ受信処理によりメッセージが取り出されたときに、メッセージが取り出されたことにより空いた領域に、メッセージの送信待ちをしているメッセージデータを新たに格納します。

メッセージバッファに送信メッセージデータを書き込めたら、送信完了待ちをしているタスクを起床します。

これをメッセージバッファに送信メッセージデータが格納できなくなるか、送信待ちをしているタスクがなくなるまで繰り返します。



Windowsは米国Microsoft Corporationの登録商標です。