DSHEng4 - 1 次メッセージ受信処理プログラミング

アプリケーション開発メモ
文書番号 DSHEng4-09-39003-00
DSHEng4 - 1 次メッセージ受信処理プログラミング
2009 年 9 月 (株)データマップ
目
次
1.はじめに .................................................................................
2.処理の流れ(フローチャート)..............................................................
3.ポーリングの方法 .........................................................................
3.1 タイマー周期によるポーリング........................................................
3.2 スレッドによる方法 .................................................................
4.受信メッセージの処理 .....................................................................
5.応答メッセージの準備と送信 ...............................................................
1
2
3
3
4
6
9
1.はじめに
ユーザプログラム(APP)における、ホストから送信される 1 次メッセージの受信ならびに 2 次メッセージ
応答までのプログラミングについて説明します。
APP は、ホストから送信されてくる全ての 1 次メッセージを、DSHEng4 が提供する受信メッセージキューを
通して取得します。
取得した後、それを処理し、そしてユーザ作成 DLL プログラムの応答関数を通して 2 次メッセージをホス
トに送信します。1、2次メッセージのトランザクションの管理は DSHEng4 が行います。
このようにすることによって、APP は、1 次メッセージに対する 2 次メッセージのトランザクション管理な
どを心配することなくプログラミングすることができます。
実際のコーディングは、デモプログラムのソースファイルを参照ください。
C# : formMain.cs、 poll.cs
VB : formMain.vb, poll.vb
処理の流れをチャートで表すと次のようになります。
APP の処理
使用関数名
DSHEngU.dll
DSHEng4.dll
1. DSHEng4 を起動時
APP が処理したい 1 次
受信 msg を登録
EngRegisterDefaultSxFy()
要求
msg を登録する
APP に渡す 1 次 MSG を
EngInitSecsMsgReq()
登録する
EngRegSecsMsg()
2. 1 次 MSG 受信
3. 受信 MSG の Polling
受信できたら処理する
Msg キュー
EngGetSecsMsgReq()
キューに put
4. 2 次 MSG 応答送信
2 次メッセージを組立て
DshResponseSxFy()
DshResponseSxFy()
Callback 処理
SxFy 応答メッセージを送信
5.送信完了 callback
1 次メッセージの登録は、ユーザが処理対象にするメッセージを決定し、DSHEngU.dll 内の
EngRegisterDefaultSxFy()関数の中にコーディングします。(u_sxfy.c 参照)
DSHEng4 は、ホストから APP に渡すように指示されているメッセージを受信した際、APP に渡すための Msg
キューを通してメッセージを渡します。
一方、APP は、EngGetSecsMsgReq()関数を使って、随時キューに受信メッセージがあるかどうかをポーリ
ングし、あれば取得し、そのメッセージの処理を行います。
処理が終ったら、DSHEng4U.dll にある応答関数を使って2次メッセージを作成し、送信します。基本的に
は、APP にとっては、応答メッセージは送りっぱなしの形になります。
1
2.処理の流れ(フローチャート)
APP が行うホストから送信されてくる1次メッセージのポーリング、取得、処理を示します。
START
DSHEng4 は準備をし、
EngStart(comm_file,
DSHDR2 HSMS 通信ドライバーを開始させ、HSMS-SS
equip_cnd,...):
通信接続処理を開始する。
DSHEng4 の起動
予約変数の登録など(EC,SV,CE)
その他の初期設定処理
APP 処理 1 次メッセージの登録
EngRegisterDefaultSxFy()
APP 受信処理対象メッセージの設定
枠内がポーリングと受信メッセージ処理です。
DSHEngU.dll
EngGetSecsMsgReq()
ポーリングで随時受信メッセージの有無を確認し、あれば
ポーリング
取得する。
受信 Msg があれば取得する
Delay
TMSG_QUEUE_INFO 構造体は次の通り。
typedef struct{
ID_TR trid;
DSHMSG *smsg;
DSHMSG **prmsg;
(未使用)
int
(WINAPI *callback)();(未使用)
} TMSG_QUEUE_INFO;
no
受信 MSG 取得した?
yes
TMSG_QUEUE_INFO 構造体に
smsg に受信 1 次メッセージが格納されている。
DSHMSG 情報とトランザクション ID を取得
受信した MSG を処理する。
DSHEngU.dll 内の2次メッセージ応答関数を呼出、送信
ACK 情報を作成し、トランザクション ID を
する。
指定して応答メッセージを送信する。
ポーリング取得した 1 次メッセージ DSHMSG のメモリの開放
DshResponseSxFy()を呼び出す。
no
は DSHEng4 が行う。
内を周期タイマーで呼び出すか、またはスレッドを
DSHEng4 終了?
使って処理する。
yes
END
2
3.ポーリングの方法
受信メッセージのポーリングは、受信し、処理するまでのタイミングが遅れないように行う必要がありま
す。受信処理は、他の処理との関係では、非同期の処理になります。
ここでは、参考までに、2つの方法を C#言語のサンプルで紹介します。
Timer、スレッドを使用する方法です。
3.1
タイマー周期によるポーリング
Visual Studio 環境では、Timer インターバルタイマーがあり、ms 単位の一定周期でのタイムアウトイベ
ントを APP に与えてくれます。デモプログラムでは、timer1 を使用し、インターバルを例えば、10ms に設定
し処理しています。
timer1.Interval = 10;
timer1.Enabled = true;
タイマーイベントのハンドラーの中で、ポーリング関数を呼び出すことになります。
ポーリングと受信メッセージ処理関数を CheckPriMsg()とすると、次のようになります。
formMain.cs ファイル
private void timer1_Tick(object sender, EventArgs e)
// タイマー1 Event Handler
{
poll.CheckPriMsg();
// Polling関数
}
poll.cs ファイル
unsafe public static void CheckPriMsg()
// ポーリング関数
{
int ei;
ei = eng_api.EngGetSecsMsgReq( ref minfo);
// any message ? あれば、minfoに情報を取得
if (ei == 0) return;
fm = formid.fm;
// fm : formMainのform
mmsg = *minfo.smsg;
// 各メッセージの処理は sxfy_proc.csファイルで行う。
ei = poll_ck_simulation(ref minfo, ref mmsg);
// formSim待機msg ?
if (ei == 0)
{
poll_msg_proc( ref minfo, ref mmsg);
// no, 一般処理
}
}
ポーリングの終了は、timer1 が Enabled=false にされたときになります。(メインプログラムが制御する)
3
3.2
スレッドによる方法
スレッド(並列処理可能なプログラム単位)を生成し、その中で、ポーリングと受信メッセージの処理を
専門に行うようにします。
スレッドの生成と起動は、using System.Threading;の中の ThreadStart()メソードを使って行い、スレッ
ドの関数名を PollThread()とすると、PollThread()は、次のような処理を行うことになります。
//----- InitPollThread() - スレッドをスタートさせる-------------------public static void InitPollThread()
{
fm = formid.fm;
poll_thread_state =State.inactive;
poll_thread = new Thread( new ThreadStart(PollThread));
poll_thread.Start();
fm.OutLog("
!! poll_thread create\n");
}
スレッド本体は、次のようになります。
//----- PollThread() - スレッド本体-----------------------------------private static void PollThread()
{
formMain fm = poll.fm;
poll_thread_state = State.active;
fm.OutLog("
// Active
!! poll_thread start\n");
while (poll_thread_state == State.active)
{
while (poll_thread_state == State.active)
{
int ei;
ei = eng_api.EngGetSecsMsgReq(ref minfo);
// any message ?
//あれば、minfoに情報を取得
if (ei == 0) break;
mmsg = (DSHMSG)Marshal.PtrToStructure(minfo.smsg, typeof(DSHMSG));
ei = poll_ck_simulation(ref minfo, ref mmsg);
// formSim待機msg ?
if (ei == 0)
{
poll_msg_proc(ref minfo, ref mmsg);
// no, 一般処理
}
}
if (poll_thread_state == State.stop_wait) break;
Thread.Sleep(10);
// 時間をおいて際チェック
}
fm.OutLog("
** poll thread end\n");
poll_thread_state = State.inactive;
// Inactive
}
4
ポーリングスレッドの終了は、メインプログラムが DSHEng4 エンジンを終了させる際に、次の関数を呼び
出して行います。
//----- TermPollThread() - スレッド終了させる- -----------------------public static void TermPollThread()
{
if (poll_thread_state == State.active)
{
poll_thread_state = State.stop_wait;
poll_thread.Join();
fm.OutLog("
!! poll thread end\n");
}
}
詳しくは、デモプログラムの 次のソースファイル を参照ください。
C# : formMain.cs、 poll.cs
VB : formMain.vb, poll.vb
5
4.受信メッセージの処理
ポーリング関数によって、DSHEng4 から EngGetSecsMsgReq()関数を使って、TMSG_QUEUE_INFO 構造体の中
に受信情報を取得します。構造体は次の通りです。
unsafe public struct TMSG_QUEUE_INFO
{
public uint
trid;
// DSHDR2 transaction ID
public IntPtr
smsg;
//
"
primary msg info received (DSHMSG)
public IntPtr
prmsg;
//
"
response msg info(not used)
public eng_api.CallbackDefault callback; // callback funtcion (not used)
}
構造体のメンバーとして与えられる情報の中で、trid, smsg を使用します。他のメンバーは APP では現在
使用することはありません。
trid は、DSHDR2 HSMS 通信ドライバーが1次メッセージ受信時に発行するトランザクション ID であり、
2次メッセージを応答する際に、この trid を渡す必要があります。
smsg は、DSHMSG 構造体のポインタであり、受信した1次メッセージの情報が格納されています。
この中には、受信メッセージのストリーム、ファンクションコード、テキストの格納ポインタ、バイト長
などの情報が格納されています。また、メッセージのデータアイテムを順番に取り出すための管理情報も含
まれています。
詳しくは、DSHDR2 通信ドライバーの説明書を参照ください。
デモプログラムでは、smsg ポインタから DSHMSG の実体を取得するため、C#または VB が提供する
PtrToStructure()関数を使用します。
DSHMSG mmsg = Marshal.PtrToStructure(minfo.smsg, typeof(DSHMSG));
あとは、mmsg 内の stream, function コードに従って分岐し、各メッセージの処理関数を呼び出すことに
なります。
デモプログラムでは、poll_msg_proc(ref minfo, ref mmsg); 関数を使って、次のように処理します。
(次頁)
6
//----- poll_msg_proc() ----------------------------------------------//
一般処理
unsafe static void poll_msg_proc( ref TMSG_QUEUE_INFO minfo, ref DSHMSG mmsg )
{
int s = mmsg.stream;
// S-stream
int f = mmsg.function;
// F-function
switch( s ){
case 1:
// S1Fx
switch (f)
{
case 15:
s1f15_proc.s1f15( ref mmsg, ref minfo);
break;
case 17:
s1f17_proc.s1f17( ref mmsg, ref minfo);
break;
}
break;
//------------------------------------------------------------case 2:
// S2Fx
switch (f)
{
case 23:
s2f23_proc.s2f23( ref mmsg, ref minfo);
break;
case 41:
s2f41_proc.s2f41( ref mmsg, ref minfo);
break;
case 43:
s2f43_proc.s2f43( ref mmsg, ref minfo);
break;
case 45:
s2f45_proc.s2f45( ref mmsg, ref minfo);
break;
case 49:
s2f49_proc.s2f49( ref mmsg, ref minfo);
break;
}
break;
(
中
略
)
break;
//------------------------------------------------------------}
eng_lib.DshFreeMsg(minfo.smsg);
}
7
以下、各処理関数で、処理を行います。が、その際に DSHEng4 が準備するメッセージデコード関数を利用
することができます。
例えば、S15F13 のデコード関数は、DshDecodeS15F13()関数を使いますが、複数のデータアイテムを含む
リスト構造のメッセージにはデコード関数が用意されています。
DshDecodeSxFy()です。ここで、x はストリーム、y はファンクションコードになります。
デコードされた結果は、それぞれのメッセージのために用意された情報格納構造体内に格納されます。APP
は含まれている情報を構造体のメンバーを指定してデータを取得し、処理することができます。
詳しくは、DSHEng4 ライブラリ関数の説明書を参照ください。
処理が終了したら、5.の2次メッセージの応答処理を行います。
なお、受信したメッセージを実際に処理するプログラムが複数存在し、一応、どのメッセージをどのプロ
グラムに渡したらいいかがポーリングプログラムが分かっている場合は、各プログラム個別のメッセージキ
ューを設け、渡す方法があります。この場合、この別立てのキューは、APP が準備します。
polling
1 次メッセージを受信する。
メッセージの配信先を判断し、
キュー 1
配信先のキューに渡す。
プログラム-1
処理
キュー 2
プログラム-2
処理
キュー.
キュー n
プログラム-.
処理
プログラム-n
DSHEng4 はキューのための機能を実現する関数を提供します。
DshInitList(), DshTermList(),DshPutList(),DshGetList() です。
8
処理
5.応答メッセージの準備と送信
処理が終った後、2次メッセージを応答する必要がありますが、まず、応答情報を準備する必要がありま
す。ACK 情報1個だけであれば、簡単ですが、可変的なリスト構造を有するちょっと面倒なメッセージもあ
ります。
DSHEng4 では、関連する処理を易しくプログラミングするための関数を提供しています。
S15F13 メッセージを例にとって、説明します。
応答情報は、TRCP_ERR_INFO 構造体に設定し、それをユーザ作成 DLL 内の DshResponseS15F14()関数を使
ってメッセージ応答送信処理を行います。
(1) まず、次の関数を使って、TRCP_ERR_INFO 構造体の初期設定を行います。
void DshInitTRCP_ERR_INFO(
ref TRCP_ERR_INFO rcp_errinfo,
// 応答情報格納構造体
int rmack,
// ack
int errcount );
// エラーパラメータ情報の数
(2)次に、
(1)で、errcount に指定した数だけ、エラー情報を次の関数を使って設定します。
int DshPutTRCP_ERR_INFO(
ref TRCP_ERR_INFO errinfo,
// 応答情報格納構造体
int errcode,
// エラーコード
byte[] errtext );
// エラーテキスト
(3)その上で、ユーザ作成 DLL の次の関数を使って応答メッセージを送信します。
int DshResponseS15F14(
uint trid,
// トランザクション ID
ref TRCP_INFO info,
// レシピ情報格納構造体
ref TRCP_ERR_INFO erinfo );
// 応答情報格納構造体
ここで、trid は、4.で説明しました TMSG_QUEUE_INFO 構造体のメンバーで、S15F13 メッセージを取得し
たときに与えられるトランザクション ID です。
本関数では、erinfo で与えられた情報から S15F14 メッセージを組立てた上で、DshResponseSxFy()関数を使
って相手に送信します。これで、1 個のメッセージの処理が終ります。
ここでは、S15F13 を例として説明しましたが、S14F9, S16F11 などについても同様に応答情報構造体が準
備されており、また、応答情報の設定関数が準備されていますので、それらを使って応答メッセージの作成
と送信を行うことができます。
9