前回の誌上セミナーでは、構造解析プログラミングの基本に加えて、FRAME面内SDKの概要と活用方法について解説しました。続く今回は、FRAME面内SDKが提供する関数を使用して実際に計算条件を入力し、計算結果をテキストファイルに出力するまでのサンプルプログラムについて解説します。
|
FRAME面内SDKのサンプルプログラムは、以下のファイルで構成されています。なお、このサンプルプログラムの実行には、FRAME面内SDKに同梱の「FrameSDK.DLL」「FrameSDKHeader.pas」の2ファイルが別途必要になります。
■表1 FRAME面内SDK サンプルファイルの構成 |
FrameSample.dpr |
サンプルプログラムのプロジェクトファイル |
FileUtil.pas |
ファイル入出力クラス |
InputDataSample.txt |
入力データのサンプル |
OutputSample.F11〜.F71 |
計算結果出力のサンプル |
SampleMain.pas |
メインフォーム(ソースコード部) |
SampleMain.dfm |
メインフォーム(ソースコード部) |
FRAME面内SDKが提供する関数は、以下の3つのみです。また、実際に計算機能を提供するのはFrameCalc関数1つだけになります。FRAME面内SDK利用の流れとしては、まずFR_SDK_MAINDATA型構造体に入力条件をセットしていき、FrameCalc関数に構造体のポインタを渡します。計算結果の取得はPointer型の戻り値から逆参照する形で行います。
■表2 FRAME面内SDKの提供する関数 |
FrameCalc関数 |
面内解析計算の実行および実行結果を返す。 |
FrameResMemFree関数 |
計算結果領域のメモリを開放する。
通常は、FrameCalc関数の後に呼び出す。 |
FrameErr関数 |
FrameCalc関数の計算中に発生したエラーの詳細情報を取得。 |
今回のサンプルプログラムではFrameSDKHeader.pas内で一括でインポートされているので、そこでの記述について簡単な解説を行います。インポート部の記述を単一のファイルにまとめてしまえば、別アプリケーションで同様の機能を利用する際に、DLLとセットでこのpasファイルをuses節に追加するだけでよいので、作業が容易になります。
■FrameSDKHeader.pas
unit FrameSDKHeader;
:
(中略)
:
implementation
// Frame Calc
function FrameCalc ( lpszFramePath : PChar;
lpstAgInData : PFR_SDK_MAINDATA ) : Pointer; stdcall;
external 'FrameSDK.dll';
// Free the Result Data Area
procedure FrameResMemFree ( lpstAgOutData : Pointer ) ;
stdcall;
external 'FrameSDK.dll';
// Get Error Information
function FrameErr ( lpErrMsg : PPChar ) : Integer ;
stdcall;
external 'FrameSDK.dll';
end;
|
上記は前述の3つの関数を、FrameSDK.dllからインポートしている例ですが、通常アプリケーション内で関数を宣言する際とは、"stdcall"と"external"という記述がある点で異なっています。stdcallとは呼び出し規約と呼ばれるもので、パラメータをルーチンに渡す順序や例外処理の方法などを指定しています。external宣言は外部ルーチン(関数)を呼び出すオブジェクトファイルまたはライブラリ名を指定しています。
サンプルプログラムの画面構成は下図のようになっています。イベントは各TButtonコンポーネントに対し設定されています。
|
(1)ButtonFrameDir |
: |
TButton |
|
(6)EditFileOut |
: |
TEdit |
(2)EditFrameDir |
: |
TEdit |
|
(7)ButtonCalc |
: |
TButton |
(3)ButtonFileIn |
: |
TButton |
|
(8)OpenDialogFrameDir |
: |
TOpenDialog |
(4)EditFileIn |
: |
TEdit |
|
(9)OpenDialogFileIn |
: |
TOpenDialog |
(5)ButtonFileOut |
: |
TButton |
|
(10)SaveDialogFileOut |
: |
TSaveDialog |
■図1 FRAME面内SDKの提供する関数 |
(1)〜(5)までのボタンについては、クリック時にOpenDialogまたはSaveDialogを実行し、それぞれFRAME面内本体のインストールフォルダ、入力データのファイル名、計算結果の保存先ファイル名を指定させ、確定時に隣接する(2)〜(6)までのEditに値を代入しています。
ここでは、サンプルプログラムで使用する入力データファイルの大まかな書式と、読み込みに使用するDelphiのクラスについて説明します。
|
■図2 入力データモデル例(BOXカルバート、構造図・荷重・支点条件) |
■入力データ例(inputDataSample.txt)
FRAME-02
TITLE FrameSDKSample
CONTROL1 格点 部材 材質 断面 バC バネ 支C
支点
6 7 1 2 1 0
1 0
CONTROL2 荷重 組合 抽出 二重 剛S 剛B
3 2 1 0 0 0
CONTROL3 リナ Mmax 着点 割増 プレ
1 0 0 0 0
POINT
6
1
0.0000 4.0000
2
5.0000 4.0000
3
10.0000 4.0000
4
0.0000 0.0000
5
5.0000 0.0000
6
10.0000 0.0000
:
(中略)
:
END
|
入力データファイルの中身は、まず開始カード(FRAME-02)から始まり、タイトル(TITLE)、コントロールデータ(CONTROL1〜3)、座標データ(POINT)、材質データ、断面データというように、値が順番に定義されています。値と値との間は半角スペースで区切られています。これを1行ずつ読み込み、行を列に分割するという流れで値を読み込んでいく際に使うのが、Textfile変数とTStringListクラスです。
Textfile変数はファイルをメモリ上に取り込んで1行ずつ読み込むのに使用し、行をさらに列単位に分割するのにTStringListを利用します。
具体的な利用方法については、実際に以下のコードを見ながら説明していきます。
■TFrameFileClass.LoadFromFile(FileUtils.pas)
57 function TFrameFileClass.LoadFromFile( FileName:string;
var FrameData: FR_SDK_MAINDATA ):boolean;
58 var
59 i : integer;
60 j : Integer;
61 txtfile : TextFile;
62 Line : String;
63 Title : String;
64 Fields : TStringList;
65 begin
66 Result := False;
67 Fields := TStringList.Create;
68 AssignFile( txtfile, FileName );
69 Reset( txtfile );
70 try
71 // FRAME-02
72 Readln( txtfile, Line
);
73
74 // TITLE
75 Readln( txtfile, Line
);
76 Title := Copy( Line,
11, 40 );
77 StrCopy( FrameData.aszCalcTitle, PAnsiChar(Title) );
:
:
: (中略)
:
:
109 // POINT
110 Readln( txtfile, Line
);
111 SetLength( NodeData,
FrameData.nNodeCnt );
112 for i := 0 to FrameData.nNodeCnt-1
do
113 begin
114
Readln( txtfile, Line );
115
Fields.CommaText := Line;
116 NodeData[i].nNodeNo := StrToInt(Fields.Strings[0]);
117 NodeData[i].dbXPoint :=
StrToFloatDef(Fields.Strings[1], 0.000 );
118 NodeData[i].dbYPoint :=
StrToFloatDef(Fields.Strings[2], 0.000 );
119 end;
120 FrameData.lpNode :=
@NodeData[0];
:
:
: (中略)
:
:
330 Result := True;
331 finally
332 CloseFile( txtfile );
333 Fields.Free;
334 end;
335 end;
|
■LoadFromFile関数内で定義される変数
変数名 |
型 |
用途 |
i, j |
Integer |
ループ変数 |
txtfile |
TextFile |
入力データファイルを行ごとに読み込むために使用 |
Line |
AnsiString |
1行分のデータを格納するための文字列 |
Title |
AnsiString |
タイトルを格納するための文字列 |
Fields |
TStringList |
Lineを分割するために使用 |
LoadFromFile関数内では以下の変数を定義しています。
これらの変数は、入力データファイルの「FRAME-02」で始まる開始カードから「END」で終わる終了カードまでの読み込み中に共通で使用します。
■各行の解説
先ほどのコードの中から、タイトルデータ、座標データを例として取り上げ、処理の内容について説明します。
まず、68〜69行目にかけてAssignFile手続きでtxtfile変数と入力データファイルとを関連付けています。これ以降のtxtfile変数に対しての操作は、すべてFileNameで指定された入力データファイル上で行われます。次にReset手続きでファイルを開いています。このとき、ファイルの位置はファイルの先頭となります。73行目からファイルを1行ずつ読み取っていきますが、このときにReadln手続きを使用しています。
Readln手続きを呼び出すと、第2引数に指定した文字列変数(この場合Line)に現在の位置からの1行分のテキスト行が格納されます。
ファイル位置が次の行の先頭に移動しているので、次にReadln手続きを呼び出した際には、Line変数には次の行のデータが格納されます。
このように、行の取り出しはReadln手続きのみを使用していきます。
続いてタイトルデータを読み込みます。入力ファイルのフォーマット上で行の11文字目〜50文字目まで(40文字)をタイトルとしているため、Copy関数を使って11文字目から40文字を読み込んでいます。
Title := Copy( Line, 11, 40 ); |
次に座標データを読み込みます。まず110行目でReadln手続きを実行した時点で、Lineには"POINT
6"という値が格納されています。
このうち"6"という数値については格点数を示していますが、格点数については座標データよりも前に入力データファイルの3〜4行目にあるコントロールデータの行で既に定義されているので、サンプルデータではその情報を使用しており、ここではそのまま読み飛ばしています。
SetLength( NodeData, FrameData.nNodeCnt
); |
さらに、SetLength関数で動的配列NodeDataの要素数(長さ)を格点数分割り当てていますが、NodeDataについてはTFrameFileClassのprivate部で次のように宣言しています。
NodeData
: array of FR_SDK_NODE; |
NodeDataはFR_SDK_NODE型構造体の動的配列として宣言しています。構造体のサイズを指定したところで、119行目から実際にfor文で格点数分をループし、構造体に格点情報をセットしていきます。
for i := 0 to FrameData.nNodeCnt-1 do
begin
Readln( txtfile, Line );
Fields.CommaText := Line;
NodeData[i].nNodeNo := StrToInt(Fields.Strings[0]);
NodeData[i].dbXPoint := StrToFloat(Fields.Strings[1]);
NodeData[i].dbYPoint := StrToFloat(Fields.Strings[2]);
end;
FrameData.lpNode := @NodeData[0]; |
ここでもReadln手続きにより、1行分の文字列をLine変数に格納します。この時点で、Line変数には以下のような値が格納されます。
■121行目のReadlnの実行結果
Line = "
1 0.0000 4.0000" |
この時点ではLine変数に格納されている値は単一の文字列に過ぎません。これを「1」「0.0000」「4.0000」の3つの値に分解するには、TStringListクラスのCommaTextプロパティを利用します。CommaTextプロパティに文字列を代入すると、スペースや","(カンマ)などを区切り文字として自動的にStringsというString型の配列プロパティにセットされます。
■122行目のCommaTextの代入結果
Fields.Strings[0] = "1"
Fields.Strings[1] = "0.0000"
Fields.Strings[2] = "4.0000"
|
これ以降は、FieldsのStringsプロパティに格納された文字列の値を、StrToInt関数やStrToFloat関数などを使用して数値型にキャストしたうえで、NodeDataレコードのメンバ変数にセットし、NodeDataレコードをFrameDataレコードに格納して、格点データの読み込みは終わりとなります。
FrameCalc関数から受け取った計算結果をテキストファイルに出力する例について説明します。
ここでは、入力データファイルの読み込みとは対照的に、ポインタの値を参照してテキストファイルに書き出していきます。
以下のような書式で格点変位(基本荷重ケース)の計算結果をファイルに出力するまでの処理を取り上げて解説します。
■格点変位(基本荷重ケース)の計算結果出力例
格点変位
CASE 1 6
1 -0.013
-1.071 -0.186
2 -0.049
-0.963 0.000
3 -0.085
-1.071 0.186
4 0.000
-1.016 0.197
5 -0.049
-0.842 0.000
6 -0.099
-1.016 -0.197
格点変位
CASE 2 6
1 -0.109
-0.259 -0.083
2 -0.110
-0.133 0.089
3 -0.110
0.015 0.032
4 0.000
-0.247 0.112
5 0.001
-0.113 -0.004
6 0.001
0.016 0.019
:
:
: (後略)
|
サンプルプログラムでは、出力するファイルごとに関数が分けられており、本節で取り扱う格点変位の計算結果出力は、TFrameFileClassクラス内のResultSetF21ToFile手続き内で行っています。FrameResDataはFrameCalc関数からの計算結果を格納するPFR_SDK_MAINRES型のポインタ変数で、クラスのprivate節で定義しています。
■TFrameFileClass.ResultSetF21ToFile(FileUtil.pas)
procedure TFrameFileClass.ResultSetF21ToFile;
var
i, j : Integer;
outputSL : TStringList;
F21DIS : PFR_SDK_F21DIS;
F21NodeDis : PFR_SDK_NODEDIS;
begin
outputSL := TStringList.Create;
F21DIS := FrameResData.lpF21Dis;
try
for i := 0 to FrameResData.nF21DisCnt-1
do
begin
outputSL.Add(
'格点変位' );
outputSL.Add(
Format( 'CASE %4d %4d',
[F21DIS^.nFndmntlLdCaseNo, F21DIS^.nNodeCnt ] ) );
F21NodeDis := F21DIS^.lpNodeDis;
for j := 0 to F21DIS^.nNodeCnt-1
do
begin
outputSL.Add( Format(
'%5d %14.3f %14.3f %14.3f',
[ F21NodeDis^.nNodeNo,
F21NodeDis^.dbHorzDsplcmnt,
F21NodeDis^.dbVertDsplcmnt,
F21NodeDis^.dbRevDsplcmnt
] ) );
Inc( F21NodeDis
);
end;
Inc( F21DIS
);
end;
outputSL.SaveToFile( ChangeFileExt( FFileName, '.F21' ) );
finally
outputSL.Free;
end;
end;
|
ポインタであるFrameResDataの示す先には、レコード型のデータが格納されており、格点変位の計算結果はこのレコード内のlpF21Disというレコード型のポインタを参照することで取得できます。ここでのポイントは、FrameResDataからは格点データのアドレスのみを受け取り、実際に格点数分のデータを参照する際にはF21DISという同じ型の別のポインタ変数を使用しているという点です。
最初に、F21DISにFrameResData内の格点変位データのポインタを代入しています。この時点でF21DISの参照先には1ケース目の荷重ケースの格点情報が格納されます。F21DISには荷重ケースNoと格点数といったヘッダ情報が含まれており、格点変位はF21DISの参照レコード内にあるポインタlpNodeDisの参照先に格納されています。ここでも同じ型のポインタ変数F21NodeDisを用意して、444行目にてlpNodeDisのポインタを代入しています。
この時点でF21NodeDisには、荷重ケース1の1点目の格点変位情報が格納されています。後は、for文で格点数分でループさせて、F21NodeDisから荷重ケース1の全格点数分の格点変位を参照していけばよいことになりますが、F21NodeDisは配列型のポインタではありません。
では、どのようにして残りの格点の計算結果を参照するのでしょうか。
forループ内では、ポインタF21NodeDisに対してInc手続きを行っています。Inc手続きは、数値を加算(インクリメント)するのによく使用しますが、引数にポインタを指定した場合はポインタの位置がポインタの型のサイズ分移動します。F21NodeDisには格点数分のレコード用のメモリ領域が確保されており、Inc手続きを行うことでポインタの位置が次の要素の先頭に移動しています。これによって、Inc手続き後にF21NodeDisを参照した際には、次の格点の変位情報を取得しています。
さらに、ポインタからレコードのメンバ変数の値を取り出し、Format関数を使って書式を整えた上で1行ずつ、TStringListのインスタンスクラスであるOutputSLに格納していきます。1行ずつの書き出しには、TStringlistクラスのAdd関数を使用し、最後に同じくTStringListクラスのSaveToFile関数を使用してファイルに出力します。
次回の誌上セミナーは、「構造解析プログラミング講座(3)」になります。建築構造解析(Mutiframe)のオートメーションを利用したDelphiプログラムの開発について解説する予定です。引き続きご期待ください。
有償セミナーのお知らせ
エンジニアのプログラミング入門セミナー CPD認定6.2pt |
● 日時 |
2011年 6月3日(金) 9:30〜16:30 |
● 受講費 |
1名様 15,750円(税込) |
● 本会場 |
フォーラムエイト東京本社 GTタワーセミナールーム
※TV会議システムにて東京・大阪・名古屋・福岡にて同時開催 |
|
|
(Up&Coming '11 新緑の号掲載) |
|
|
>> 製品総合カタログ
>> プレミアム会員サービス
>> ファイナンシャルサポート
|