RPGのプログラマーもICFを使ったコーディングは、苦手なかたも多いのではないでしょうか?
ICFFは、APPC,BSC、非同期、などの異なるプロトコルでも、同じPGMインターフェースで
PGMの作成を可能にします。残念ながらTCPIPのSocketは無理ですが...
ICFFの構造は、複数のレコード様式に、使用したい通信機能に対応したキーワードをセットして書き出す
ことで、セッションの開始/終了、データの送受信、受信確認などが可能になります。
プログラミングのイメージは、画面のPGMに非常によく似ており、実際F仕様書ではWORKSTNと
定義します。
実際に通信環境を使ってPGMを動かすのは大変だと思いますので、ここでは、AS/400の
INTRAって言う便利な機能を使って、通信のPGMを動かしてみたいと思います。
INTRAを使うと、起動PGM/受動PGMが1台のAS/400の中で実行できるようになります。
AS/400が、ICFを介して、実際の通信をシミュレートします。
INTRAの定義方法
CRTDEVINTR DEVD(INTRA) RMTLOCNAME(INTRA) TEXT('CALB SAMPLE Y.IDE')
WRKCFGSTS *DEV INTRA +実行キー
INTRA装置をオンに構成変更にしてください。
これで定義は終わりです。以外と簡単でしょ!
それではまず、ICFファイルを作成してください
A INDARA
A RCVENDGRP(80)
A RCVDETACH(81)
A R INV INVITE
A R IOREC
A FLD01 128O
A R EVOKE
A SECURITY(2 &PASS 3 &USERID)
A EVOKE(&LIB/&PGMID)
A PASS 8A
A USERID 8A
A LIB 8A
A PGMID 8A
A R DETACH DETACH
|
作成コマンドは以下の様になります。
CRTICFF FILE(CLAB/ICF1CF) SRCFILE(CLAB/QICFSRC)
MAXPGMDEV(256)
起動側プログラムが使用するPGMDEVの登録
ADDICFDEVE FILE(CLAB/ICF1CF) PGMDEV(INTRA)
RMTLOCNAME(INTRA) CMNTYPE(*INTRA)
受動側プログラムが使用するPGMDEVの登録
ADDICFDEVE FILE(CLAB/ICF1CF) PGMDEV(INTRARCV)
RMTLOCNAME(*REQUESTER) CMNTYPE(*INTRA)
次に、起動側RPGを作成してください
H Y/ 1
H*---------------------------------------------------------------*
H* PROGRAM-ID : ICF1R
H* REMARKS : ICFソース側プログラム
H* AUTHOR : Y.IDE
H* DATE-WRITEN : 98/09/27
H* VERSION : 01.00 ORIGINAL *
H*---------------------------------------------------------------*
FICF1CF CF E WORKSTN
F KINFDS FEEDBK
F KNUM 1
F KID PGMDEV
FICF1FM CF E WORKSTN
IFEEDBK DS
I 38 45 FMTNM
I 273 282 PGMDEV
I 401 402 MAJCOD
I 403 404 MINCOD
C EXFMTSC01
C*-------*
C 'INTRA' ACQ ICF1CF
C MOVEL'INTRA' PGMDEV 10
C*-------*
C MOVEL'CLABPW' PASS
C MOVEL'CLABID' USERID
C MOVEL'CLAB' LIB
C MOVEL'ICF2CL' PGMID
C WRITEEVOKE
C*-------*
C 1 DO *HIVAL
C WRITEINV
C READ IOREC 90
C*
C MOVELFLD01 FLD1
C*
C *IN80 IFEQ *ON
C LEAVE
C ENDIF
C *IN81 IFEQ *ON
C LEAVE
C ENDIF
C*
C EXFMTSC02
C ENDDO
C*
C EXFMTSC03
C SETON LR
C RETRN
C*
|
ICF1FM
A CF03(03)
A R SC01
A 6 3'通信開始します'
A R SC02
A 6 2'受信データ'
A FLD1 80O O 7 2
A R SC03
A 6 3'通信終了しました'
|
[プログラムの解説]
DSPFに通信を開始しますと表示します。実行キーを押されると、次のステップにすすみます
'INTRA' ACQ ICF1CF
ICFファイルに登録してあるINTRAというDEVを獲得します
ADDICFDEVE FILE(CLAB/ICF1CF) PGMDEV(INTRA)
のPGMDEVの名前に対応します。
MOVEL'INTRA' PGMDEV 10
INTRAのDEVを省略時のデバイスとして設定します。
2つ以上のDEVをACQで獲得した場合は、PGMDEVを切り替えてそれぞれ通信が出来ます。
MOVEL'CLABPW' PASS........パスワード
MOVEL'CLABID' USERID......ユーザーID
MOVEL'CLAB' LIB...............ライブラリー
MOVEL'ICF2CL' PGMID.......呼び出すPGM
WRITEEVOKE
ユーザーID/パスワード/呼び出すPGM/ライブラリーをセットして、EVOKE命令を実行することで通信が開始します。この段階で、サブシステムQCMN等に、呼び出したPGMのJOBが乗ってきます。
WRITEINV
READ IOREC
INVITEキーワードを指定したINVレコード様式をwriteします。INVITEは送信勧誘を表し、相手からデータを送ることを要求します。INVレコード様式には、フィールドがありませんので、空のレコードで送信勧誘のみが送られます。
送信勧誘とREADを永久LOOPし、標識80or81がオンになるとプログラムが終了します。
80がオンになる条件はRCVENDGRP(80)....通信グループの終了
81がオンなる条件はRCVDETACH(81).....通信の切断
通信相手からのDEATCHを受けるまで、ループは続きます。
受信したデータは、順次画面に表示します。実行キーを押すことで、次のreadを行います
受動側CLとRPGを作成してください
/* PROGRAM-ID : ICF2CL */
PGM
ADDLIBLE LIB(CLAB)
CALL PGM(ICF2R)
ENDPGM
|
直接RPGを呼びたしてももちろん構わないのですが、今回は、ライブラリーリストにCLABを入れるためだけに
CLを経由してRPGをCALLしています。そのままRPGをCALLするとICFFが見つからないためRPGが
異常終了します。
ICF2R
H Y/ 1
H*---------------------------------------------------------------*
H* PROGRAM-ID : ICF2R
H* REMARKS : ICFターゲット側プログラム
H* AUTHOR : Y.IDE
H* DATE-WRITEN : 98/09/27
H* VERSION : 01.00 ORIGINAL *
H*---------------------------------------------------------------*
FICF1CF CF E WORKSTN
F KINFDS FEEDBK
F KNUM 1
F KID PGMDEV
IFEEDBK DS
I 38 45 FMTNM
I 273 282 PGMDEV
I 401 402 MAJCOD
I 403 404 MINCOD
C*-------*
C 'INTRARCV'ACQ ICF1CF
C MOVEL'INTRARCV'PGMDEV 10
C*-------*
C READ IOREC 9999
C*-------*
C 1 DO 5 X 30
C MOVE *BLANK FLD01
C MOVEL' 件'FLD01
C MOVELX FLD01
C WRITEIOREC
C*
C *IN80 IFEQ *ON
C LEAVE
C ENDIF
C *IN81 IFEQ *ON
C LEAVE
C ENDIF
C ENDDO
C*
C WRITEDETACH
C*
C SETON LR
C RETRN
C*
|
[プログラムの解説]
'INTRARCV'ACQ ICF1CF
ICFファイルに登録してあるINTRARCVというDEVを獲得します
ADDICFDEVE FILE(CLAB/ICF1CF) PGMDEV(INTRARCV)
RMTLOCNAME(*REQUESTER) CMNTYPE(*INTRA)
*REQUESTERは呼びだしもとのロケーションを受け入れます。
READ IOREC
WRITE IOREC
WRITE DETACH
起動側RPGをC言語で書いて見ましょう。
/*--------------------------------------------------------------*/
/* PROGRAM-ID : ICF1C */
/* REMARKS :ICFファイルI/O */
/* AUTHOR : Y.Ide */
/* DATE-WRITEN : 98/10/04 */
/* VERSION : 01.00 ORIGINAL */
/*--------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <recio.h>
#include <QSYSINC/H/QSNAPI>
#pragma mapinc("icf","CLAB/ICF1CF(*ALL)","both","d","icf","FM")
#include "icf"
#pragma mapinc("ind","CLAB/ICF1CF(*ALL)","indicators","")
#include "ind"
_RFILE *icff;
void ioCheck(char *majorRc){
if ( memcmp(majorRc, "00", 2) != 0 ){
printf("0308はDETACH受信\n");
printf("majorRc = %s \n",majorRc);
_Rclose(icff);
exit(0);
}
}
int main(void)
{
_RIOFB_T *fb; /*オープンフィードバック*/
Qsn_Ssn_T ssn;
FM_EVOKE_both_t evoke;
char fld01[129];
int rc,i;
_SYSindara ind_area;
ssn = _C_Get_Ssn_Handle();
rc = QsnClrScl(ssn,'0',NULL);
printf("AS/400 CLAB ICF SAMPLE \n");
/* ICFのオープン*/
if (( icff = _Ropen("ICF1CF", "ar+,indicators=Y" )) == NULL )
{
printf ("OPEN ERROR\n" );
exit ( 1 );
}
/*装置獲得*/
if ( (_Racquire(icff, "INTRA ")) == 0){
printf("ACQ DEV ERROR\n");
}
/*省略時のPGMDEV SET */
if ( (_Rpgmdev(icff, "INTRA ")) == 0){
printf("PGMDEV SET ERROR\n");
}
/*フィールド設定*/
memcpy(evoke.PASS, "CLABPW ", 8);
memcpy(evoke.USERID,"CLABID ", 8);
memcpy(evoke.LIB, "CLAB ", 8);
memcpy(evoke.PGMID, "ICF2CL ", 8);
/*個別標識を設定する*/
_Rindara (icff,ind_area);
/*レコード様式を設定する*/
_Rformat(icff,"EVOKE" );
fb = _Rwrite(icff, &evoke, 32);
ioCheck(fb->sysparm->_Maj_Min.major_rc);
while(1){
_Rformat(icff, "INV");
fb = _Rwrite ( icff, "", 0 );
_Rformat(icff, "IOREC");
fb = _Rreadn(icff, fld01, 128, __DFT);
ioCheck(fb->sysparm->_Maj_Min.major_rc);
printf("%s\n",fld01);
}
_Rclose (icff);
}
|
[プログラムの解説]
#pragma mapinc("icf","CLAB/ICF1CF(*ALL)","both","d","icf","FM")
#include "icf"
#pragma mapinc("ind","CLAB/ICF1CF(*ALL)","indicators","")
#include "ind"
外部記述レコードを利用して、Cの構造体を作成し、インクルードします。
内容を参照するには、*SHOWSURを指定してコンパイルしてください。
indicatorsは標識のエリアです。ICFにINDARAキーワードを指定すると、
標識が独立したエリアに取られます。指定しない場合は、各レコード様式内に含まれます。
void ioCheck(char *majorRc){
if ( memcmp(majorRc, "00", 2) != 0 ){
printf("0308はDETACH受信\n");
printf("majorRc = %s \n",majorRc);
_Rclose(icff);
exit(0);
}
}
|
通信のリターンコード(メジャー/マイナー)をチェックする関数(サブルーチン)です。
’00’以外の場合は、ICFをクローズします。
ssn = _C_Get_Ssn_Handle();
rc = QsnClrScl(ssn,'0',NULL);
DSM(ダイナミック・スクリーン・マネージャー)画面をクリアします
_Racquire(icff, "INTRA ")
_Rpgmdev(icff, "INTRA ")
fb = _Rwrite(icff, &evoke, 32);
ioCheck(fb->sysparm->_Maj_Min.major_rc);
_Rformat(icff, "INV");
fb = _Rwrite ( icff, "", 0 );
_Rformat(icff, "IOREC");
fb = _Rreadn(icff, fld01, 128, __DFT);
ioCheck(fb->sysparm->_Maj_Min.major_rc);
通信のREAD/WRITE毎にioCheck()関数(サブルーチン)を呼び、リターンコードのチェックを行う
printf("%s\n",fld01);
受信したデータの表示を行う。RPGではDSPFを使用したが、Cでは標準出力を利用する、
|