📄 modlinkdemomain.pas
字号:
//--------------------------------------------------------------------------------------------------
var
TransactionLogFileName: TFileName = '';
//--------------------------------------------------------------------------------------------------
function GetTransactionLogFileName: TFileName;
begin
if TransactionLogFileName = EmptyStr then
TransactionLogFileName := ExtractFilePath(ParamStr(0)) + 'TransactionLog.txt';
Result := TransactionLogFileName;
end;
//--------------------------------------------------------------------------------------------------
const
ServerReplies: array [TServerReply] of string = (
{ srNone } 'Transaction timed out (no reply)',
{ srInvalidFrame } 'Transaction timed out (reply frame is corrupted)',
{ srUnexpectedReply } 'Transaction timed out (reply from unexpected server)',
{ srUnmatchedReply } 'Transaction failed (reply does not match request)',
{ srExceptionReply } 'Transaction failed (server returned exception reply)',
{ srNormalReply } 'Transaction succeeded'
);
//--------------------------------------------------------------------------------------------------
procedure ValidateNumberInEditBox(AEdit: TEdit; MinValue, MaxValue: Int64);
// begin of local block --------------------------------------------------------------------------
procedure Error(ResStringRec: PResStringRec; const Args: array of const);
begin
{$IFDEF COMPILER_5_UP}
raise Exception.CreateResFmt(ResStringRec, Args);
{$ELSE}
raise Exception.CreateFmt(LoadResString(ResStringRec), Args);
{$ENDIF COMPILER_5_UP}
end;
procedure InvalidInteger;
begin
if AEdit.CanFocus then AEdit.SetFocus;
Error(@SInvalidInteger, [AEdit.Text]);
end;
procedure InvalidRange;
begin
if AEdit.CanFocus then AEdit.SetFocus;
Error(@SOutOfRange, [MinValue, MaxValue]);
end;
// end of local block ----------------------------------------------------------------------------
var
I: Int64;
begin
try
I := StrToInt64(AEdit.Text);
if (MinValue <> MaxValue) and ((I < MinValue) or (I > MaxValue)) then
InvalidRange;
except
on E: Exception do
if E is EConvertError then
begin
InvalidInteger;
end
else
raise;
end;
end;
//--------------------------------------------------------------------------------------------------
{$IFNDEF COMPILER_5_UP}
// FreeAndNil doesn't exist in Delphi 4.
procedure FreeAndNil(var Obj);
var
TempObj: TObject;
begin
TempObj := TObject(Obj);
Pointer(Obj) := nil;
TempObj.Free;
end;
{$ENDIF COMPILER_5_UP}
//--------------------------------------------------------------------------------------------------
// TModLinkDemoMainForm class
//--------------------------------------------------------------------------------------------------
constructor TModLinkDemoMainForm.Create(AOwner: TComponent);
const
SIntroductionFileName = 'Introduction.rtf';
begin
inherited;
AccessSettings(GetSettingsFileName, LoadSettings);
ToolsLogTransactionsToFileItem.Checked := fLogTransactionsToFile;
LogString('');
if fLogTransactionsToFile then
LogString('*** Transactions are now being logged to both screen and file.')
else
LogString('*** Transactions are now being logged to screen only.');
LogString('');
LogString('*** Welcome to demonstration of ModLink VCL component suite!');
LogString(Format('*** Running on ModLink version %s', [ModLinkVersion]));
LogString('');
LogString('*** To specify in which connection mode to operate:');
LogString('*** [1] Select ''Tools -> Modbus Connection Options...'' from the menu.');
LogString('*** [2] A dialog will appear.');
LogString('*** [3] Go to ''Modbus Transaction Management'' page.');
LogString('*** [4] Locate ''Connection Mode'' group box.');
LogString('*** [5] Select ''Client'' radio button to operate as local client.');
LogString('*** [6] Select ''Server'' radio button to operate as local server.');
LogString('*** [7] Click OK button to close the dialog and apply the changes.');
LogString('');
LoadServerItems(GetServerItemsFileName);
try
try
ModbusConnection1.Open;
except
on E: Exception do
begin
LogString(Format('Failed to open Modbus connection. %s'#13#10, [E.Message]));
{$IFDEF COMPILER_6_UP}
if Assigned(ApplicationHandleException) then
ApplicationHandleException(Self);
{$ELSE}
Application.HandleException(Self);
{$ENDIF}
end;
end;
finally
ConnectionModeChanged;
UpdateConnectionStatus;
end;
Application.HintHidePause := 20000;
PageControl1.ActivePage := IntroductionTabSheet;
RegisterListView.Column[0].Index := 1;
UpdateDiscreteListView;
UpdateRegisterListView;
try
IntroductionRichEdit.Lines.LoadFromFile(ExtractFilePath(ParamStr(0)) + SIntroductionFileName);
except
{$IFDEF COMPILER_6_UP}
if Assigned(ApplicationHandleException) then
ApplicationHandleException(Self);
{$ELSE}
Application.HandleException(Self);
{$ENDIF}
end;
end;
//--------------------------------------------------------------------------------------------------
destructor TModLinkDemoMainForm.Destroy;
begin
fLogFile.Free;
inherited;
end;
//--------------------------------------------------------------------------------------------------
procedure TModLinkDemoMainForm.BeginLogTransactions;
var
OpenMode: Word;
begin
if not Assigned(fLogFile) then
begin
if FileExists(GetTransactionLogFileName) then
OpenMode := fmOpenReadWrite or fmShareDenyWrite
else
OpenMode := fmCreate or fmShareDenyWrite;
fLogFile := TFileStream.Create(GetTransactionLogFileName, OpenMode);
fLogFile.Seek(0, soFromEnd);
end;
Inc(fLogCounter);
end;
//--------------------------------------------------------------------------------------------------
procedure TModLinkDemoMainForm.ClearTransactionLog;
begin
LogMemo.Clear;
if fLogTransactionsToFile then
begin
BeginLogTransactions;
try
Assert(Assigned(fLogFile));
fLogFile.Size := 0;
finally
EndLogTransactions;
end;
end;
end;
//--------------------------------------------------------------------------------------------------
procedure TModLinkDemoMainForm.ConnectionModeChanged;
const
ConnectionModes: array [TConnectionMode] of string = ('CLIENT', 'SERVER');
begin
Caption := Format('%s [LOCAL %s MODE]', [Application.Title,
ConnectionModes[ModbusConnection1.ConnectionMode]]);
ToolsClientOptionsItem.Visible := ModbusConnection1.ConnectionMode = cmClient;
ToolsServerOptionsItem.Visible := ModbusConnection1.ConnectionMode = cmServer;
ToolsDiscardPendingTransactionsItem.Visible := ModbusConnection1.ConnectionMode = cmClient;
RegisterAccessTabSheet.TabVisible := ModbusConnection1.ConnectionMode = cmClient;
DiscreteAccessTabSheet.TabVisible := ModbusConnection1.ConnectionMode = cmClient;
DiagnosticsTabSheet.TabVisible := ModbusConnection1.ConnectionMode = cmClient;
ServerMapTabSheet.TabVisible := ModbusConnection1.ConnectionMode = cmServer;
end;
//--------------------------------------------------------------------------------------------------
procedure TModLinkDemoMainForm.EditSelectedServerItem;
var
ListItem: TListItem;
begin
with ServerItemsListView do
begin
ListItem := Selected;
if Assigned(ListItem) then
begin
if EditServerItem(PServerItem(ListItem.Data), False) then
begin
SaveServerItems(GetServerItemsFileName);
UpdateServerItem(ListItem);
end;
end
else
Beep;
end;
end;
//--------------------------------------------------------------------------------------------------
procedure TModLinkDemoMainForm.EndLogTransactions;
begin
if fLogCounter > 0 then
Dec(fLogCounter);
if fLogCounter = 0 then
FreeAndNil(fLogFile);
end;
//--------------------------------------------------------------------------------------------------
function TModLinkDemoMainForm.FindServerItemByAddress(Address: Word; ItemKind: TItemKind): PServerItem;
var
I: Integer;
ServerItem: PServerItem;
begin
Result := nil;
for I := 0 to ServerItemsListView.Items.Count - 1 do
begin
ServerItem := PServerItem(ServerItemsListView.Items[I].Data);
if (ServerItem^.Addr = Address) and (ServerItem^.Kind = ItemKind) then
begin
Result := ServerItem;
Break;
end;
end;
end;
//--------------------------------------------------------------------------------------------------
procedure TModLinkDemoMainForm.LoadServerItems(const aFileName: string);
var
vStream: TFileStream;
vCount, I, X: Integer;
vServerItem: PServerItem;
vListItem: TListItem;
begin
ServerItemsListView.Items.Clear;
if FileExists(aFileName) then
begin
vStream := TFileStream.Create(aFileName, fmOpenRead or fmShareDenyWrite);
try
ServerItemsListView.Items.BeginUpdate;
try
{ Obtain the number of server items to be read from the stream }
vCount := vStream.Size div SizeOf(TServerItem);
for I := 0 to Pred(vCount) do
begin
New(vServerItem);
try
LoadServerItem(vServerItem, vStream);
vListItem := ServerItemsListView.Items.Add;
try
for X := 0 to 4 do
vListItem.SubItems.Add('');
vListItem.Data := Pointer(vServerItem);
UpdateServerItem(vListItem);
except
ServerItemsListView.Items.Delete(vListItem.Index);
raise;
end;
except
Dispose(vServerItem);
raise;
end;
end;
finally
ServerItemsListView.Items.EndUpdate;
end;
finally
vStream.Free;
end;
end;
end;
//--------------------------------------------------------------------------------------------------
procedure TModLinkDemoMainForm.LoadSettings(aIniFile: TCustomIniFile);
const
cDefaultPort = 'COM1';
begin
fLogTransactionsToFile := aIniFile.ReadBool(gcGeneral, gcLogTransactionsToFile, False);
with ModbusConnection1 do
begin
BaudRate := TBaudRate(aIniFile.ReadInteger(gcModbusConnection1, gcBaudRate, Ord(br19200)));
if BaudRate = brCustom then
CustomBaudRate := Cardinal(aIniFile.ReadInteger(gcModbusConnection1, gcCustomBaudRate, 19200));
ConnectionMode := TConnectionMode(aIniFile.ReadInteger(gcModbusConnection1, gcConnectionMode, Ord(cmClient)));
DataBits := TDataBits(aIniFile.ReadInteger(gcModbusConnection1, gcDataBits, Ord(db8)));
DTREnabled := aIniFile.ReadBool(gcModbusConnection1, gcDTREnabled, True);
EchoQueryBeforeReply := aIniFile.ReadBool(gcModbusConnection1, gcEchoQryBeforeRpy, False);
FlowControl := TFlowControl(aIniFile.ReadInteger(gcModbusConnection1, gcFlowControl, Ord(fcNone)));
MaxRetries := TMaxRetries(aIniFile.ReadInteger(gcModbusConnection1, gcMaxRetries, 1));
Parity := TParityScheme(aIniFile.ReadInteger(gcModbusConnection1, gcParity, Ord(psEven)));
Port := aIniFile.ReadString(gcModbusConnection1, gcPort, cDefaultPort);
ReceiveTimeout := Cardinal(aIniFile.ReadInteger(gcModbusConnection1, gcReceiveTimeout, 1000));
RefetchDelay := Cardinal(aIniFile.ReadInteger(gcModbusConnection1, gcRefetchDelay, 0));
RTSEnabled := aIniFile.ReadBool(gcModbusConnection1, gcRTSEnabled, True);
SendTimeout := Cardinal(aIniFile.ReadInteger(gcModbusConnection1, gcSendTimeout, 1000));
SilentInterval := Cardinal(aIniFile.ReadInteger(gcModbusConnection1, gcSilentInterval, 4));
StopBits := TStopBits(aIniFile.ReadInteger(gcModbusConnection1, gcStopBits, Ord(sb1)));
ThreadPriority := TThreadPriority(aIniFile.ReadInteger(gcModbusConnection1, gcThreadPriority, Ord(tpNormal)));
TransmissionMode := TTransmissionMode(aIniFile.ReadInteger(gcModbusConnection1, gcTransmissionMode, Ord(tmRTU)));
TurnaroundDelay := Cardinal(aIniFile.ReadInteger(gcModbusConnection1, gcTurnaroundDelay, 100));
end;
ModbusClient1.ServerAddress := aIniFile.ReadInteger(gcModbusClient1, gcServerAddress, 1);
ModbusServer1.Address := aIniFile.ReadInteger(gcModbusServer1, gcAddress, 1);
end;
//--------------------------------------------------------------------------------------------------
procedure TModLinkDemoMainForm.LogBroadcast;
begin
LogString('Broadcasting mode: No reply is expected to be returned from a remote server(s).');
end;
//--------------------------------------------------------------------------------------------------
procedure TModLinkDemoMainForm.LogDone(ID: Cardinal; const CmdDesc: string);
begin
LogString(Format('DONE: %s [ID: %d]', [CmdDesc, ID]));
end;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -