第5章  ICFFも使えるの?
RPGプログラマーの為のC言語講座

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

    呼びたしもとからのEVOKEを受け入れます

WRITE IOREC

    5回データを送信します

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 ")
    RPGのACQ命令に同じ
_Rpgmdev(icff, "INTRA ")
    RPGのPGMDEVに同じ
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では標準出力を利用する、