Windows版 iTRONサービスコールの作成 (メッセージバッファ)
(その4)

メッセージを送信する

メッセージバッファへメッセージを送信するサービスコールは以下のとおりです。


サービスコール名説明
snd_mbf

タイムアウトなしでメッセージバッファへメッセージを送信します。

psnd_mbf

ポーリング方式でメッセージバッファへメッセージを送信します。

tsnd_mbf

タイムアウト付きでメッセージバッファへメッセージを送信します。

メッセージバッファへメッセージを送信する (タイムアウトなし)

タイムアウトなしでメッセージバッファへメッセージを送信するには以下のサービスコールを使用します。



    ER snd_mbf( ID mbfid, VP msg, UINT msgsz )
	

引数説明
mbfid

メッセージバッファID番号。

msg

メッセージバッファに格納するメッセージのポインタ。

msgsz

メッセージバッファに格納するメッセージのサイズ。

戻り値説明
E_OK

正常終了。

E_ID

範囲外のメッセージバッファID番号。

E_NOEXS

指定したメッセージバッファID番号は登録されていない。

E_PAR

パラメータエラー。

E_CTX

非コンテキスト・タスクからの呼び出し。


・サービスコールのソースコードは以下のようになります。


/****************************************************************************/
/*!
 *  @brief  メッセージバッファへの送信 (タイムアウトなし).
 *
 *  @param  [in]    mbfid   メッセージバッファID番号.
 *  @param  [in]    msg     メッセージのポインタ.
 *  @param  [in]    msgsz   メッセージのサイズ.
 *
 *  @retval エラーコード.
 */
ER      snd_mbf( ID mbfid, VP msg, INT msgsz )
{
    //! タイムアウトなしでメッセージバッファへメッセージを送信する.
    return tsnd_mbf( mbfid, msg, msgsz, TMO_FEVR );
}
	

tsnd_mbf()のタイムアウト設定に TMO_FEVR を指定して呼び出します。

メッセージバッファに空きが無い場合は、メッセージバッファに空きできるまでタスクをスリープさせます。

メッセージバッファへメッセージを送信する (ポーリング)

ポーリングでメッセージバッファへメッセージを送信するには以下のサービスコールを使用します。



    ER psnd_mbf( ID mbfid, VP msg, UINT msgsz )
	

引数説明
mbfid

メッセージバッファID番号。

msg

メッセージバッファに格納するメッセージのポインタ。

msgsz

メッセージバッファに格納するメッセージのサイズ。

戻り値説明
E_OK

正常終了。

E_ID

範囲外のメッセージバッファID番号。

E_NOEXS

指定したメッセージバッファID番号は登録されていない。

E_PAR

パラメータエラー。

E_TMOUT

メッセージバッファに空きが無い。


・サービスコールのソースコードは以下のようになります。


/****************************************************************************/
/*!
 *  @brief  メッセージバッファへの送信 (ポーリング).
 *
 *  @param  [in]    mbfid   メッセージバッファID番号.
 *  @param  [in]    msg     メッセージのポインタ.
 *  @param  [in]    msgsz   メッセージのサイズ.
 *
 *  @retval エラーコード.
 */
ER      psnd_mbf( ID mbfid, VP msg, UINT msgsz )
{
    //! 送信待ちなしでメッセージバッファへメッセージを送信する.
    return tsnd_mbf( mbfid, msg, msgsz, TMO_POL );
}
	

tsnd_mbf()のタイムアウト設定に TMO_FEVR を指定して呼び出します。

メッセージバッファに空きが無い場合は、タスクをスリープさせずに戻り値をE_TMOUTとしてすぐにサービスコールから戻ります。

メッセージバッファへメッセージを送信する (タイムアウト付き)

タイムアウト付きでメッセージバッファへメッセージを送信するには以下のサービスコールを使用します。



    ER tsnd_mbf( ID mbfid, VP msg, INT msgsz, TMO tmout )
	

引数説明
mbfid

メッセージバッファID番号。

msg

メッセージバッファに格納するメッセージのポインタ。

msgsz

メッセージバッファに格納するメッセージのサイズ。

tmout

タイムアウトの設定値。

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

戻り値説明
E_OK

正常終了。

E_ID

範囲外のメッセージバッファID番号。

E_NOEXS

指定したメッセージバッファID番号は登録されていない。

E_TMOUT

メッセージバッファに空きが無い。

E_PAR

パラメータエラー。

E_CTX

非コンテキスト・タスクからの呼び出し。


・サービスコールのソースコードは以下のようになります。


/****************************************************************************/
/*!
 *  @brief  メッセージバッファへの送信 (タイムアウトあり).
 *
 *  @param  [in]    mbfid   メッセージバッファID番号.
 *  @param  [in]    msg     メッセージのポインタ.
 *  @param  [in]    msgsz   メッセージのサイズ.
 *  @param  [in]    tmout   タイムアウト設定.
 *
 *  @retval エラーコード.
 */
ER      tsnd_mbf( ID mbfid, VP msg, UINT msgsz, TMO tmout )
{
    ER ercd;

    wi_CommonLock();

    //! メッセージバッファへメッセージを送信する.
    ercd = wi_SendMsgBuffer( mbfid, msg, msgsz, tmout );

    wi_CommonUnlock();
    return ercd;
}
	

指定したメッセージバッファID番号のメッセージバッファへメッセージを格納します。

メッセージバッファに空きが無い場合は、メッセージバッファに空きができるか、タイムアウトで設定した時間が経過するまでタスクをスリープさせます。

メッセージバッファへメッセージを格納する関数

メッセージバッファへメッセージを格納する関数のソースコードは以下のとおりです。



/****************************************************************************/
/*!
 *  @brief  メッセージ・バッファへの送信.
 *
 *  @param  [in]    id      メッセージバッファID番号.
 *  @param  [in]    msg     メッセージのポインタ.
 *  @param  [in]    size    メッセージのサイズ.
 *  @param  [in]    tmout   タイムアウト設定.
 *
 *  @retval エラーコード.
 */
ER      wi_SendMsgBuffer( INT id, VP msg, INT size, TMO tmout )
{
    ER          ercd;
    WIMBFOBJ    *p;
    WITSKOBJ    *tsk1,*tsk2;

    //! メッセージバッファIDのオブジェクトを取得する.
    p = (WIMBFOBJ *)wi_FindObject( id, TMAX_MAXMBF, ObjList, &ercd );
    if( !p ){
        return ercd;
    }
    //! 引数が不正な場合はエラーにする.
    if( !msg || !size ){
        return E_PAR;
    }
    //! メッセージ待ちをしているタスクがある場合.
    if( p->RcvWaitQue ){

        //! メッセージ待ちをしているタスクにメッセージを渡す.
        tsk1 = (WITSKOBJ *)(p->RcvWaitQue);
        if( tsk1->Param[0] ){
            memcpy( tsk1->Param[0], msg, size );
            tsk1->Param[1] = (VP)size;
        }
        //! メッセージ待ちをしているタスクを起床する.
        wi_TaskWakeup( tsk1->Hdr.Id, TTW_RMBF, &(p->RcvWaitQue) );
        return E_OK;
    }
    //! 自タスクのタスクコンテキストを取り出す.
    tsk1 = wi_GetTaskObject( TSK_SELF );

    //! 非タスクコンテキストからの呼び出しの場合はエラーにする.
    if( !tsk1 ){
        return E_CTX;
    }
    //! 送信待ちのタスクがある場合.
    if( p->SndWaitQue ){

        /*!
         * 待ち行列がタスク優先度順の場合、待ち行列の先頭のタスクの優先度より
         * 今回のタスク(自タスク)の優先度が大きいければ、メッセージバッファに
         * 待ち行列のタスクよりも先に送信メッセージを格納する.
         */
        if( p->SndAttr & TA_TPRI ){
            tsk2 = (WITSKOBJ *)(p->SndWaitQue);
            if( tsk1->TaskPri > tsk2->TaskPri ){
                if( PutMesageData( p, msg, size ) ){
                    return E_OK;
                }
            }
        }
    }else{
        //! メッセージバッファの最後に送信メッセージを格納する.
        if( PutMesageData( p, msg, size ) ){
            return E_OK;
        }
    }
    //! タスクコンテキストに送信メッセージを格納しておく.
    tsk1->Param[0] = msg;
    tsk1->Param[1] = (VP)size;

    //! バッファが空くまでタスクをスリープさせる.
    return wi_TaskWait( id, TTW_SMBF, tmout, p->SndAttr, &(p->SndWaitQue) );
}
	

メッセージバッファへメッセージを格納する関数は以下のような処理を行います。

  • 引数で指定されたメッセージバッファID番号に該当するメッセージバッファ・オブジェクトを取り出します。
  • メッセージ受信待ちをしているタスクがあれば、送信メッセージを受け渡して正常終了します。このとき受信待ちをしているタスクを起床します。
  • 他にメッセージ送信待ちのタスクがある場合、メッセージ待ち行列の先頭のタスクよりもタスク優先度が高いときは、他のタスクよりも先にメッセージを格納します。
    先頭のタスクよりタスク優先度が低い場合、バッファが空くか、タイムアウトになるまでタスクをスリープさせます。
  • メッセージ待ちをしているタスクがない場合、メッセージバッファの最後にメッセージを追加します。
    メッセージバッファにメッセージを追加できなかった場合、バッファが空くか、タイムアウトになるまでタスクをスリープさせます。


/****************************************************************************/
/*!
 *  @brief  メッセージバッファにメッセージを格納する.
 *
 *  @param  [in]    p       メッセージバッファ・オブジェクトのポインタ.
 *  @param  [in]    msg     メッセージを格納した領域のポインタ.
 *  @param  [in]    len     メッセージのサイズ.
 *
 *  @retval TRUE = OK. / FALSE = NG.
 */
static BOOL PutMesageData( WIMBFOBJ *p, BYTE* msg, INT len )
{
    INT i;
    INT free_size;

    //! メッセージバッファに空きがない場合は処理を終了する.
    free_size = GetFreeSize( p );
    if( free_size < len ){
        return FALSE;
    }
    /*!
     * メッセージ・サイズの途中でリングバッファを折り返えすかもしれないので
     * メッセージ・サイズを1バイトづつバッファに格納する.
     */
    for( i = 0; i < 4; i++ ){
        static int shift[4] = { 24, 16, 8, 0 };

        //! メッセージのサイズを1バイトづつ格納する.
        p->MsgBuf[p->PutPos] = (BYTE)((((DWORD)len) >> shift[i]) & 0xFF);

        //! バッファの書き込み位置を更新する.
        p->PutPos = wi_IncRingPositon( p->PutPos, p->MsgSize );
    }
    //! メッセージがリングバッファを折り返す場合は2回に分けてメッセージを格納する.
    if( (p->PutPos + len) > p->MsgSize ){
        DWORD size1,size2;

        //! リングバッファの最後にメッセージの先頭データをコピーする.
        size1 = p->MsgSize - p->PutPos;
        memcpy( &p->MsgBuf[p->PutPos], msg, size1 );

        //! リングバッファの先頭に残りのメッセージをコピーする.
        size2 = len - size1;
        memcpy( p->MsgBuf, &msg[size1], size2 );

        //! バッファの書き込み位置を更新する.
        p->PutPos = size2;
    }else{
        //! バッファにメッセージをコピーする.
        memcpy( &p->MsgBuf[p->PutPos], msg, len );

        //! バッファの書き込み位置を更新する.
        p->PutPos += len;
    }
    return (INT)len;
}
	

メッセージバッファには、「メッセージのバイト数」+「メッセージデータ」の形式に送信メッセージを格納します。

メッセージバッファはリングバッファにしていあるので、「メッセージのバイト数」をバッファの領域から、はみ出さないように1バイトづつ書き込みます。

「メッセージデータ」の方は、1バイトづつだと効率が悪いので、データの書き出し位置からバッファの先頭にまたがるかどうかを判定して、2回に分けて書き込みます。



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