Rational Open Access:RPG Edition のワークショップ パート3
この記事の前の 2 つの記事では、RPG プログラムをコーディングして Rational Open Access: RPG Edition (RPG OA) ハンドラー・プログラムを扱う方法についてお話ししました。プログラムの例では、ハンドラーがどのようにしてどの RPG 命令コードがプログラムを起動するか判断し、データを戻す、RPG ステータス・コードを戻す、または %EOF や %ERROR などの組み込み関数値を設定することでハンドラーがどのように命令コードに応答するかを示しました。「Rational Open Access: RPG Edition ワークショップ パート 2」で示した IFS03 プログラム例はハンドラー・プログラム IFSHDLR3 を呼び出し、ハードコーディングされた値を使用してデータを IFS03 に戻します。
本号では、IBM i の IFS にあるテキスト・ファイルから読み取ったデータを戻すよう、ハンドラー・プログラムに最後の仕上げをします。また、クライアント・プログラムがその他のパラメーターをハンドラーに渡すことができる RPG OA 技法を使用します。
IFS テキスト・ファイルをマッピングする
本号のプログラム例では、特定のフォーマットで作成されたテキスト・ファイルを扱うハンドラー・プログラムを作成しました。図1 はファイルの内容の例を示しています。qcustcdt.txt ファイルは IFS directory/rpgoa にあります。ファイルを作成するため、IBM i Access File Download プログラムを使用して、内容を QIWS/QCUSTCDT データベース・ファイルから自分の PC の ASCII テキスト・ファイルにダウンロードしました。次に、PC からテキスト・ファイルを IFS にコピーしました。テキスト・ファイルを作成したときに使用した具体的な手順は重要ではありません。あなたが、同じテキスト・ファイル例を作成できるように手順を示しているだけです。
テキスト・ファイルがどのように作成されるか完全に管理できていたため、テキスト・ファイル内の各データ要素の開始と終了の正確な位置を決めました。おわかりのように、これらの位置はハンドラー・プログラムでハードコーディングされています。実行時にこれらの位置を抽出するハンドラーを開発するのが好ましいのは明らかですが、このシリーズは RPG OA 技法の使用方法を調べることが目的なので、コードを追加してテキスト・ファイル内の位置を動的に決定することは、この記事の範囲を越えていると判断しました。
テキスト・ファイルでデータのマッピングを開始するため、データベース・ファイル QIWS/QCUSTCDT の Display File Field Description (DSPFFD) コマンドを使用しました。DSPFFD コマンドの出力では、11 個のフィールドがあります。固定長文字フィールドが 5 つ、ゾーン 10 進数フィールドが 6 つです。DSPFFD 出力で示すフィールド長を使用して、テキスト・ファイルのフィールドをマップピングすることができます。
DSPFFD 出力はフィールド長とバッファー位置を示していますが、DSPFFD 値を使用してテキスト・ファイルのフィールド位置は判断できません (図1)。テキスト・ファイルでは、各数値フィールドの前にはブランク文字があります。ブランク文字は符号文字用です。QCUSTCDT ファイルのすべての数値フィールドには正の値があるため、どの数値フィールドにも負の符号文字は存在しません。RPG ハンドラー・コードから、テキスト・ファイルのフィールドの開始位置を決定する際に、符号文字を考慮しなければならなかったことがわかります。
RPG からテキスト・ファイルにアクセスする
RPG を使用して IFS ファイルを処理する場合に最も便利なオプションは、Scott Klement が彼のサイトで提供しているコードです (http://www.scottklement.com/)。
この記事では、サービス・プログラム IFSTEXTR4 を使用しています。このプログラムは、writeline と readline の 2 つのプロシージャーをエクスポートします。テキスト・ファイルから読み取っているだけなので、RPG OA ハンドラーで readline プロシージャーだけ呼び出す必要があります。
Scott は自分のコードの指示を一式、その利用方法の例を説明した文書とともに提供しています。ここでは、彼の指示をまねるよりは、彼の Web サイトにあるコードとそれを扱う場合に必要なその他の情報を参照します。
プログラムとハンドラーを調べる
この記事のオンライン版でご覧いただける Web の図1(bit.ly/YICA4Q)は、IFS04 プログラムを示しています。これは、このシリーズのパート2 で使用した IFS03 プログラムの修正版です。プログラム IFS04 への唯一の変更点はコールアウト A にあります。ここでは、ハンドラー・プログラムは IFSHDLR4 に設定されています。IFS04 プログラムのソース・コードをダウンロードし ( http://www2.systeminetwork.com/code/)、CRTBNDRPG コマンドを使用してコンパイルできます。
新しいハンドラー・プログラムは IFSHDLR4 (図2) です。このハンドラーはパート 2 の IFSHDLR3 ハンドラー・プログラムより複雑に見えますが、RPG OA 処理のコードは同じです。IFSHDLR4 のその他のコードは IFS のテキスト・ファイルを読み取ります。
図2 のコールアウトは以下を示しています。
- コールアウトA: このセクションにはバインディング・ディレクトリーと IFSEBOOK ルーチンで使用される copy ステートメントが入っています。
- コールアウトB: qcustcdt 外部定義データ構造は、クライアント・プログラム (IFS04) からハンドラーに渡されるレコード・フォーマットを提供します。以前のバージョンのハンドラー (IFSHDLR3) では、qcustcdt ファイルは F 仕様を使用して定義されました。このハンドラーはデータベース・ファイルから読み取っていないことを強調するため、F 仕様の代わりに外部定義データ構造が使用されています。データ構造は QOA.inputBuffer ポインターに基づいている点に注意してください。つまり、このレコード・フォーマット領域はクライアント・プログラムとハンドラー間で渡されます。
- コールアウトC: このセクションには IFS readline プロシージャーで使用されるフィールドが含まれています。textData フィールドの長さは、 IFS テキスト・ファイルの "record" の長さに設定されています。この長さは、その符号文字の各数値フィールドの前にある先頭スペースを組み込んでいます。
- コールアウトD: エラー・コードは、名前の付いた固定情報を使用して定義されています。エラー・コード (01217) の値とその名前は、 RPG リファレンス・マニュアルの Exception/Error Codes 表からピックアップされています。
- コールアウトE: このコードは OPEN 命令を処理します。この場合、OPEN 命令は RPG "オープン" 命令コードではなく、IFSIO_H コピー・ファイルに定義された OPEN プロシージャーへの呼び出しです。ファイルの説明 (fd) がゼロ未満の場合、IFS ファイルのオープン時にエラーがあった場合、rpgStatus サブフィールドにエラー値が設定されます。オープンするファイルの名前はハードコーディングされている点に注意してください (/rpgoa/qcustcdt.txt)。この記事の後半で、ファイル名を「ソフトコーディング」する方法を示します。
- コールアウトF: readline プロシージャーは、 IFSTEXT_H コピー・ファイルで定義されています。このプロシージャーは、データの "read" 長を textData フィールドに読み込んでいます。呼び出しから readline に戻る際、readLength は 1 未満になります。つまり、テキスト・ファイルのすべてのデータが読み取られたことを意味します。readLength が 1 以上の場合、textData フィールドにデータがあることを意味しています。
- コールアウトG: このセクションのコードは、textData フィールドのデータを分割し、フィールド値を qcustcdt データ構造に割り当てています。各 %subst 命令の開始位置は、符号文字の先頭の空白を考慮して DSPFFD 出力からのフィールド長とフィールド・タイプ (数値または 10 進数) により決定されます。
- コールアウトH: IFS ファイルの close プロシージャーが呼び出されています。close プロシージャーのプロトタイプは、 IFSIO_H コピー・ファイルに指定されています。
IFS04 クライアント・プログラム同様、CRTBNDPRG コマンドを使用して IFSHDLR4 プログラムを作成できます。両方のプログラムを作成した後、IFS にテキスト・ファイル /rpgoa/qcustcdt.txt があることを確認し、IFS04 プログラムを呼び出します。テキスト・ファイルから読み取った各レコードの値を示すスプール・ファイル出力がご覧いただけると思います。
自分のパラメーターを渡す
ハンドラー・プログラムは現在動作しており、IFS のテキスト・ファイルからデータを読み取っています。
残念ながら、IFSHDLR4 プログラムはハードコーディングされたテキスト・ファイルから読み取ります。
図2 のコールアウト E では、IFS オープン・パラメーターに渡された最初のパラメーターは、テキスト・ファイルのパスと名前が入ったテキスト・リテラルです。ファイル名を変数にし、オープンされるファイルの名前を変数に割り当てても問題はありません。割り当てる値がどこから来るのか判断することが問題です。
おそらく、ハンドラー・プログラムにデータベース・ファイルからレコードを読み取らせて、処理するテキスト・ファイルの名前を取得したり、データ域から値を取得したりできるでしょう。プログラマーには、テキスト・ファイルの名前をそのハンドラーに提供するクライアント・プログラムが必要でしょう。その要件を提供するため、RPG OA には QrnOpenAccess_T データ構造に userArea ポインターが組み込まれています。userArea ポインターはそのデータ構造で4番目に定義されたサブフィールドです。
userArea ポインターは、クライアント・プログラムでフィールドまたはデータ構造に割り当てられています。この記事のオンライン版でご覧いただける Web の図2(bit.ly/YICA4Q) は、私が定義した ifsDS_t データ構造を示しています。IFS にファイルの名前を含んでいるデータ構造サブフィールド (pathFileName) は一つしかありません。そのため、この例でも単一のフィールドだけ使用しています。しかし、単一のサブフィールド・データ構造を使用することに不都合な点はありません。データ構造を配置することで、今後必要になったら、クライアントからハンドラーに渡すことができるサブフィールドを簡単に追加できます。
データ構造の値をハンドラーに渡すために、クライアント・プログラム IFS05 (図3) を作成しました。このクライアント・プログラムでは、IFS04 (Web の図1) と比べて3つの変更点があります。変更点は次のとおりです。
- コールアウト A: handler キーワードにはオプションの第2パラメーターが含まれています。このパラメーターは、ハンドラー・プログラムに渡すフィールド名またはデータ構造名を示します。この場合、データ構造名は ifsDS です。ハンドラー・プログラムの名前は、下記 IFSHDLR5 に設定されている点に注意してください。
- コールアウト B: このコードはデータ構造の定義をコピーし、名前 ifsDS をデータ構造に割り当てます。
- コールアウト C: IFS テキスト・ファイルのパスおよびファイル名は ifsDS.pathFileName サブフィールドに割り当てられています。
IFS05 クライアントの残りのコードは IFS04 クライアントと同じです。
パスとファイル名は IFS05 プログラムにハードコーディングされていますが、他の技法を使用して割り当てた値を簡単に取得できます。例えば、パスとファイル名をその *ENTRY パラメーター・リストの一部としてクライアント・プログラムに渡したり、対話式プログラムのユーザー入力から値を取得したりできます。ポイントは、IFS 作業ファイルがクライアント・プログラムで特定され、その結果ハンドラーからハードコーディングされたファイル名を削除できるということです。
改訂された最終版のハンドラーは IFSHDLR5 (図4) です。図4 のコールアウトでおわかりのように、このハンドラーへの変更は IFSHDLR4 (図2) に比べて最小限に抑えられています。
- コールアウト A: このコードにはデータ構造定義が入っています。データ構造は、ポインター QOA.userArea に基づいて定義されている点に注意してください。ハンドラーがクライアントから起動されると、クライアントの ifsDS データ構造の値は、ポインター経由でハンドラーにアクセスできるようになります。クライアント・プログラムは ifsDS データ構造をポインターに明示的に割り当てる必要はありませんでした。それは、データ構造名を handler キーワードの第 2 パラメーターとして指定するだけで、クライアントが面倒を見ました (図3 のコールアウト A)。
- コールアウト B: IFS open プロシージャーは、オープンするファイルの名前として ifsDS.pathFileName サブフィールドの値を使用します。
おわかりのように、データ構造でサブフィールドを追加で定義できなくするものは何もありません。クライアント・プログラムでサブフィールドの値を設定することができ、サブフィールドは呼び出されたときにハンドラーにアクセスできます。
仕上げをする
このシリーズの記事がRPGでOpen Accessを扱う場合の基本事項を理解するお役に立てたことと思います。自分のハンドラーを開発する場合、このシリーズで示してきた付加的アプローチを採用すると便利なことがおわかりかと思います。ハンドラーに処理を加えすぎる前に、RPGクライアント・プログラムからの呼び出しに応答する処理を1つ作成します。ハンドラーがクライアントに応答したら、ハンドラーに必要な機能を追加できます。