MELSEC MX Component を Delphi XE5 で使う その2 2016/ 7/25 変更
2017/ 1/11
※サンプルコードの Suspend(一時停止)、Resume(再開)は、最近の Delphi では使用不可(非推奨)になっています。
また、Synchronize() の使い方が、適切ではありません。
■FX5UCPU EThernet ポート直結のサンプルです。
・起動と同時に通信を開始します。
・LAN ケーブル抜け、PLC 電源断等で、通信異常が発生した時は、2 秒周期で、再接続を試みます。
・通信正常時は、100 ミリ秒周期で、デバイスの値を取得します。
・マルチスレッドを使っています。
■ ActProgType 用 ほぼ実用コード
// 2017/ 1/11 追記
// Suspend(一時停止)、Resume(再開)は、最近の Delphi では使用不可(非推奨)になっています。
// また、Synchronize() の使い方が、適切ではありません。
// 必要な個所のみ、Synchronize()を使う例
{
Synchronize(procedure() begin
Form1.Label2.Text := 'Receive Error';
ShowMessage('応答がありません.');
end);
}
// -------------------------------------------------
// メイン
// -------------------------------------------------
unit MXCompoTest2Unit;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.OleCtrls,
ActProgTypeLib_TLB, Vcl.StdCtrls;
type
TForm3 = class(TForm)
ActProgType1: TActProgType;
Timer1: TTimer;
Edit1: TEdit;
Edit2: TEdit;
Edit3: TEdit;
Edit4: TEdit;
Edit5: TEdit;
Edit6: TEdit;
Edit7: TEdit;
Edit8: TEdit;
Edit9: TEdit;
Edit10: TEdit;
Edit11: TEdit;
Edit12: TEdit;
Edit13: TEdit;
Edit14: TEdit;
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
Label1: TLabel;
Label2: TLabel;
Edit15: TEdit;
Button5: TButton;
Edit16: TEdit;
procedure Timer1Timer(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure Button5Click(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form3: TForm3;
GB_ReadCnt : integer;
GB_OpenCnt : integer;
GB_TmBusy : boolean;
GB_OpenFlag : boolean;
// スレッドを保持
Th : TThread;
// プロトタイプ
function HexToSingle(const hex: string): single;
function HexToDouble(const hex: string): double;
function SingleToHex(d: single) : String;
function DoubleToHex(const d: double): string;
function PlcBufToSingle(var iData : array of integer): single;
function PlcBufToDouble(var iData : array of Integer): double;
function PlcBufToStr(var iData: array of Integer): string;
function PlcBufToShort(var iData: array of Integer): SmallInt;
function PlcBufToInt(var iData: array of Integer): integer;
procedure ShortToPlcBuf(i : integer; var iData: array of Integer);
procedure IntToPlcBuf(i : integer; var iData: array of integer);
procedure SingleToPlcBuf(d : single; var iData: array of Integer);
procedure DoubleToPlcBuf(d : double; var iData: array of Integer);
procedure StrToPlcBuf(const s: string; var iData :array of integer);
implementation
{$R *.dfm}
uses MXCompoTest2ThreadUnit;
function PlcBufToShort(var iData: array of Integer): SmallInt;
begin
Result := 0;
if Length(iData) >= 1 then
Result := Short(iData[0]);
end;
procedure ShortToPlcBuf(i : integer; var iData: array of Integer);
begin
if Length(iData) >= 1 then
iData[0] := i and $FFFF;
end;
function PlcBufToInt(var iData: array of Integer): integer;
begin
result := 0;
if Length(iData) >= 2 then
Result := iData[1] * $10000 + iData[0];
end;
procedure IntToPlcBuf(i : integer; var iData: array of integer);
begin
if Length(iData) >= 2 then begin
iData[0] := i and $FFFF;
iData[1] := (i and $FFFF0000) div $10000;
end;
end;
//32ビット実数(単精度)変換
function HexToSingle(const hex: string): single;
var
i : Integer;
FloatValue: Single absolute i;
begin
i := StrToInt('$' + hex);
Result := FloatValue;
end;
function PlcBufToSingle(var iData : array of integer): single;
var
i : Integer;
FloatValue: Single absolute i;
begin
if Length(iData) >= 2 then
i := iData[1] * $10000 + iData[0];
result := FloatValue;
end;
// 32 ビット実数(単精度)変換
function SingleToHex(d: single) : String;
var
i : Cardinal absolute d;
begin
Result := IntToHex(i, 8);
end;
// 32 ビット実数(単精度)変換
procedure SingleToPlcBuf(d : single; var iData: array of Integer);
var
i : Cardinal absolute d;
begin
if Length(iData) >= 2 then begin
iData[0] := i and $FFFF;
iData[1] := (i and $FFFF0000) div $10000;
end;
end;
// 64 ビット実数(倍精度)変換
function DoubleToHex(const d: double): string;
var
Overlay: Int64 absolute d;
begin
result := IntToHex(Overlay, 16);
end;
procedure DoubleToPlcBuf(d : double; var iData: array of Integer);
var
Overlay: Int64 absolute d;
begin
if Length(iData) >= 4 then begin
iData[0] := Overlay and $FFFF;
iData[1] := (Overlay and $FFFF0000) div $10000;
iData[2] := (Overlay and $FFFF00000000) div $100000000;
iData[3] := (Overlay and $FFFF000000000000) div $1000000000000;
end;
end;
//64ビット実数(倍精度)変換
function HexToDouble(const hex: string): double;
var
d : double;
Overlay: array[1..2] of LongInt absolute d;
begin
result := 0.0;
if Length(hex) = 16 then begin
Overlay[1] := StrToInt('$' + Copy(hex, 9, 8));
Overlay[2] := StrToInt('$' + Copy(hex, 1, 8));
result := d;
end;
end;
// PLC 読み込みバッファを64ビット実数(倍精度)変換
function PlcBufToDouble(var iData : array of Integer): double;
var
d : double;
Overlay: array [0..1] of LongInt absolute d;
begin
result := 0.0;
if Length(iData) >= 4 then begin
Overlay[0] := iData[1] * $10000 + iData[0];
Overlay[1] := iData[3] * $10000 + iData[2];
result := d;
end;
end;
// PLC読み込み用バッファを文字列に
function PlcBufToStr(var iData: array of Integer): string;
var
arycnt : integer;
i : integer;
begin
// 配列の大きさ
arycnt := Length(iData);
result := '';
for i := 0 to arycnt - 1 do begin
result := result + Char(iData[i] and $00FF);
result := result + Char((iData[i] and $FF00) div $100);
end;
end;
// 文字列をPLC書き込み用バッファに
procedure StrToPlcBuf(const s: string; var iData :array of integer);
var
arycnt : integer;
len, i : integer;
begin
// 配列の大きさ
arycnt := Length(iData);
// 文字列の長さ
len := Length(s);
for i := 1 to len div 2 do begin
iData[i - 1] := Ord(s[i * 2 ]) * $100 + Ord(s[i * 2 - 1]);
if i = arycnt then break;
end;
if (len div 2 < arycnt) and (len mod 2 > 0) then
iData[len div 2] := Ord(s[len]);
end;
// 16 ビット整数
procedure TForm3.Button1Click(Sender: TObject);
var
iData : array [0..0] of Integer;
begin
if GB_OpenFlag then begin
ShortToPlcBuf(StrToInt(Edit11.Text), iData);
ActProgType1.WriteDeviceBlock('D0', 1, iData[0]);
end;
end;
// 32 ビット整数
procedure TForm3.Button2Click(Sender: TObject);
var
iData : array [0..1] of Integer;
begin
if GB_OpenFlag then begin
IntToPlcBuf(StrToInt(Edit12.Text), iData);
ActProgType1.WriteDeviceBlock('D0', 2, iData[0]);
end;
end;
// 32 ビット実数(単精度)
procedure TForm3.Button3Click(Sender: TObject);
var
iData : array [0..1] of Integer;
begin
if GB_OpenFlag then begin
SingleToPlcBuf(StrToFloat(Edit13.Text), iData);
ActProgType1.WriteDeviceBlock('D0', 2, iData[0]);
end;
end;
// 64 ビット実数(倍精度)
procedure TForm3.Button4Click(Sender: TObject);
var
iData : array [0..3] of Integer;
begin
if GB_OpenFlag then begin
DoubleToPlcBuf(StrToFloat(Edit14.Text), iData);
ActProgType1.WriteDeviceBlock('D0', 4, iData[0]);
end;
end;
// 文字列書き込み
procedure TForm3.Button5Click(Sender: TObject);
var
iData : array of Integer;
writelen : integer;
begin
// 書き込み数(文字列の長さは、この倍まで)
writelen := 10;
SetLength(iData, writelen);
StrToPlcBuf(Edit16.Text , iData);
ActProgType1.WriteDeviceBlock('D0', writelen, iData[0]);
end;
procedure TForm3.FormDestroy(Sender: TObject);
begin
// スレッド終了
if Assigned(Th) then Th.Terminate;
end;
// インターバルタイマー
procedure TForm3.Timer1Timer(Sender: TObject);
begin
if not GB_OpenFlag then
Timer1.Interval := 2000
else
Timer1.Interval := 100;
if not Assigned(Th) then begin
Th := PlcThread.Create;
end
else begin
// スレッド再開
Th.Resume;
end;
end;
end.
// -------------------------------------------------
// PLC オープン、データ読込スレッド
// -------------------------------------------------
unit MXCompoTest2ThreadUnit;
interface
uses
System.Classes, System.SysUtils,Winapi.Windows;
type
PlcThread = class(TThread)
private
{ Private 宣言 }
procedure Plc_Open;
procedure Plc_Read;
protected
procedure Execute; override;
end;
implementation
uses MXCompoTest2Unit;
{ PlcThead }
// PLC オープン
procedure PlcThread.Plc_Open;
var
iRet : integer;
Ticks : Cardinal;
begin
if not GB_TmBusy then begin
Inc(GB_OpenCnt);
GB_TmBusy := True;
with Form3.ActProgType1 do begin
ActUnitType := $2002; //UNIT_FXVETHER_DIREC 'ユニットタイプ
ActCpuType := $210; //CPU_FX5UCPU 'CPUタイプ
ActProtocolType := $8; //PROTOCOL_UDPIP '通信プロトコルタイプ
// 直結接続の場合、対象PLCのIPアドレスは不要
ActHostAddress := '255.255.255.255'; // 接続ホスト名(IP アドレス)文字列
ActDestinationPortNumber := $15B8;
ActTimeOut := 500; // 通信のタイムアウト値(単位は"ms")
Ticks := GetTickCount;
// オープン
iRet := Open;
Form3.Edit1.Text := IntToHex(iRet, 8);
Form3.Edit2.Text := IntToStr(GB_OpenCnt);
// すでに接続済
if iRet = -268435453 then begin
// 一度切断
Close;
// 再接続
iRet := Open;
end;
with Form3 do begin
// 読込実行時間(ms)を表示
Edit3.Text := IntToStr(GetTickCount - Ticks);
if iRet = 0 then begin
GB_OpenFlag := True;
if Label2.Caption <> '通信正常' then Label2.Caption := '通信正常';
end
else begin
if Label2.Caption <> '通信異常' then Label2.Caption := '通信異常';
end;
end;
end;
GB_TmBusy := False;
end;
end;
// PLC データ読込
procedure PlcThread.Plc_Read;
var
ReadData : array [0..3] of integer;
// 文字列取得用動的配列
ReadData2 : array of Integer;
// 読込文字数
txtlen : integer;
readlen : integer;
iRet : integer;
Ticks : Cardinal;
begin
if not GB_TmBusy then begin
Inc(GB_ReadCnt);
GB_TmBusy := True;
with Form3 do begin
Edit3.Text := IntToStr(GB_ReadCnt);
// 通信状態を表示
if Label1.Caption <> '●' then Label1.Caption := '●'
else Label1.Caption := '○';
// 開始時間を保持
Ticks := GetTickCount;
// D0 ~ D3 の値を取得
iRet := ActProgType1.ReadDeviceBlock('D0', 4, ReadData[0]);
if iRet = 0 then begin
// そのまま 16 進で表示
Edit5.Text := IntToHex(ReadData[0], 4);
Edit6.Text := INtToHex(ReadData[1], 4);
// D0 を 16ビット整数として表示
Edit7.Text := IntToStr(PlcBufToShort(ReadData[0]));
// D0, D1 を 32ビット整数として取得
Edit8.Text := IntToStr(PlcBufToInt(ReadData));
// D0, D1 を 32ビット単精度実数として表示
Edit9.Text := FloatToStr(PlcBufToSingle(ReadData));
// D0 ~ D3 を 64 ビット倍精度実数として表示
Edit10.Text := FloatToStr(PlcBufToDouble(ReadData));
// 文字列取得
txtlen := 20; // 取得する文字列の最大数
readlen := txtlen div 2;
if txtlen mod 2 > 0 then Inc(readlen);
SetLength(ReadData2, readlen);
if ActProgType1.ReadDeviceBlock('D0', readlen, ReadData2[0]) = 0 then
Edit15.Text := PlcBufToStr(ReadData2);
end
else begin
GB_OpenFlag := False;
end;
// 実行時間(ミリ秒)を表示
Edit4.Text := IntToStr(GetTickCount - Ticks) + ' msec';
end;
GB_TmBusy := False;
end;
end;
// スレッド実行
procedure PlcThread.Execute;
begin
while not Terminated do begin
if not GB_TmBusy then begin
if not GB_OpenFlag then
Synchronize(Plc_Open)
else
Synchronize(Plc_Read);
end;
Sleep(5);
// スレッド一時停止
Th. Suspend;
end;
end;
end.