2017-09-10 13:15:32 +00:00
|
|
|
unit MainFrm;
|
|
|
|
|
|
|
|
// #ToDo1 -oMvR: 2-9-2017: device change notification -> refresh COM port list
|
|
|
|
// #ToDo1 -oMvR: 2-9-2017: visualize fan power
|
|
|
|
// #ToDo1 -oMvR: 10-9-2017: save settings
|
|
|
|
|
|
|
|
interface
|
|
|
|
uses
|
|
|
|
System.Classes,
|
|
|
|
System.SysUtils,
|
|
|
|
Vcl.Controls,
|
|
|
|
Vcl.ExtCtrls,
|
|
|
|
Vcl.Forms,
|
|
|
|
Vcl.StdCtrls,
|
|
|
|
|
|
|
|
CPort,
|
|
|
|
|
|
|
|
Simulator.Registry, Vcl.Buttons;
|
|
|
|
|
|
|
|
|
|
|
|
type
|
|
|
|
TMainForm = class(TForm, ISimulatorFans)
|
|
|
|
PortComboBox: TComboBox;
|
|
|
|
HardwareGroupBox: TGroupBox;
|
|
|
|
PortLabel: TLabel;
|
|
|
|
PortStatusLabel: TLabel;
|
|
|
|
ConnectTimer: TTimer;
|
|
|
|
FansLabel: TLabel;
|
|
|
|
SimulatorGroupBox: TGroupBox;
|
|
|
|
ResponseTimer: TTimer;
|
|
|
|
FanCountLabel: TLabel;
|
|
|
|
CalibrationButton: TButton;
|
|
|
|
SimulatorComboBox: TComboBox;
|
|
|
|
SimulatorPanel: TPanel;
|
|
|
|
SimulatorSelectionPanel: TPanel;
|
|
|
|
SimulatorLockButton: TSpeedButton;
|
|
|
|
|
|
|
|
procedure FormCreate(Sender: TObject);
|
|
|
|
procedure PortComboBoxClick(Sender: TObject);
|
|
|
|
procedure ConnectTimerTimer(Sender: TObject);
|
|
|
|
procedure ResponseTimerTimer(Sender: TObject);
|
|
|
|
procedure FormClose(Sender: TObject; var Action: TCloseAction);
|
|
|
|
procedure CalibrationButtonClick(Sender: TObject);
|
|
|
|
procedure SimulatorComboBoxClick(Sender: TObject);
|
|
|
|
procedure SimulatorLockButtonClick(Sender: TObject);
|
|
|
|
private
|
|
|
|
FComPort: TComPort;
|
|
|
|
FReceived: string;
|
|
|
|
FOnResponse: TProc<string>;
|
|
|
|
FOnTimeout: TProc;
|
|
|
|
FReady: Boolean;
|
|
|
|
FFanValues: array of Byte;
|
|
|
|
FSimulator: ISimulator;
|
|
|
|
|
|
|
|
procedure LoadSimulators;
|
|
|
|
|
|
|
|
procedure RefreshPorts;
|
|
|
|
procedure InitDevice(const AInfo: string);
|
|
|
|
|
|
|
|
procedure TryConnect;
|
|
|
|
|
|
|
|
procedure SendCommand(const ACommand: string; AOnResponse: TProc<string>; AOnTimeout: TProc = nil);
|
|
|
|
procedure StartCommand;
|
|
|
|
procedure EndCommand;
|
|
|
|
procedure OnReceiveChar(Sender: TObject; Count: Integer);
|
|
|
|
|
|
|
|
function ValidFanIndex(AFan: Byte): Boolean; inline;
|
|
|
|
|
|
|
|
procedure SelectSimulator;
|
|
|
|
|
|
|
|
property ComPort: TComPort read FComPort;
|
|
|
|
property Ready: Boolean read FReady;
|
|
|
|
property Simulator: ISimulator read FSimulator;
|
|
|
|
public
|
|
|
|
{ ISimulatorFans }
|
|
|
|
function GetFanCount: Integer;
|
|
|
|
function GetReady: Boolean;
|
|
|
|
function GetIsRunning: Boolean;
|
|
|
|
|
|
|
|
function GetValue(const AFan: Byte): Byte;
|
|
|
|
function GetMaxValue: Byte;
|
|
|
|
|
|
|
|
procedure SetValue(const AFan, AValue: Byte);
|
|
|
|
procedure SetAll(const AValue: Byte);
|
|
|
|
|
|
|
|
procedure SetFull;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
implementation
|
|
|
|
uses
|
|
|
|
System.Math,
|
|
|
|
System.StrUtils,
|
|
|
|
Vcl.Dialogs,
|
|
|
|
|
|
|
|
ESCCalibrationFrm;
|
|
|
|
|
|
|
|
|
|
|
|
{$R *.dfm}
|
|
|
|
|
|
|
|
|
|
|
|
{ TMainForm }
|
|
|
|
procedure TMainForm.FormCreate(Sender: TObject);
|
|
|
|
begin
|
|
|
|
LoadSimulators;
|
|
|
|
RefreshPorts;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
|
|
|
|
begin
|
|
|
|
if Ready then
|
|
|
|
begin
|
2017-09-10 15:10:41 +00:00
|
|
|
FReady := False;
|
|
|
|
|
2017-09-10 13:15:32 +00:00
|
|
|
SendCommand('>SetFans:0',
|
|
|
|
procedure(Response: string)
|
|
|
|
begin
|
|
|
|
Close;
|
|
|
|
end);
|
2017-09-10 15:10:41 +00:00
|
|
|
|
2017-09-10 13:15:32 +00:00
|
|
|
Action := caNone;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TMainForm.PortComboBoxClick(Sender: TObject);
|
|
|
|
begin
|
|
|
|
TryConnect;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TMainForm.SimulatorComboBoxClick(Sender: TObject);
|
|
|
|
begin
|
|
|
|
SelectSimulator;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TMainForm.SimulatorLockButtonClick(Sender: TObject);
|
|
|
|
begin
|
|
|
|
SimulatorComboBox.Enabled := not SimulatorLockButton.Down;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TMainForm.CalibrationButtonClick(Sender: TObject);
|
|
|
|
begin
|
|
|
|
if Assigned(Simulator) then
|
|
|
|
Simulator.Stop;
|
|
|
|
|
|
|
|
TESCCalibrationForm.Execute(Self);
|
|
|
|
|
|
|
|
if Assigned(Simulator) then
|
|
|
|
Simulator.Start;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TMainForm.LoadSimulators;
|
|
|
|
var
|
|
|
|
simulator: TRegisteredSimulator;
|
|
|
|
|
|
|
|
begin
|
|
|
|
SimulatorComboBox.Items.BeginUpdate;
|
|
|
|
try
|
|
|
|
SimulatorComboBox.Items.Clear;
|
|
|
|
|
|
|
|
for simulator in GetRegisteredSimulators do
|
|
|
|
SimulatorComboBox.Items.AddObject(simulator.Name, simulator);
|
|
|
|
|
|
|
|
SimulatorComboBox.ItemIndex := 0;
|
|
|
|
finally
|
|
|
|
SimulatorComboBox.Items.EndUpdate;
|
|
|
|
end;
|
|
|
|
|
|
|
|
SelectSimulator;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TMainForm.RefreshPorts;
|
|
|
|
var
|
|
|
|
currentPort: string;
|
|
|
|
|
|
|
|
begin
|
|
|
|
currentPort := '';
|
|
|
|
if PortComboBox.ItemIndex > -1 then
|
|
|
|
currentPort := PortComboBox.Items[PortComboBox.ItemIndex];
|
|
|
|
|
|
|
|
EnumComPorts(PortComboBox.Items);
|
|
|
|
|
|
|
|
if Length(currentPort) > 0 then
|
|
|
|
PortComboBox.ItemIndex := PortComboBox.Items.IndexOf(currentPort);
|
|
|
|
|
|
|
|
TryConnect;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TMainForm.InitDevice(const AInfo: string);
|
|
|
|
var
|
|
|
|
info: TStringList;
|
|
|
|
value: string;
|
|
|
|
fanCount: Integer;
|
|
|
|
|
|
|
|
begin
|
|
|
|
info := TStringList.Create;
|
|
|
|
try
|
|
|
|
for value in SplitString(AInfo, ',') do
|
|
|
|
info.Add(value);
|
|
|
|
|
|
|
|
if TryStrToInt(info.Values['Fans'], fanCount) then
|
|
|
|
begin
|
|
|
|
SetLength(FFanValues, fanCount);
|
|
|
|
FanCountLabel.Caption := IntToStr(fanCount);
|
|
|
|
end else
|
|
|
|
SetLength(FFanValues, 0);
|
|
|
|
|
|
|
|
CalibrationButton.Enabled := info.Values['Mode'] = 'Servo';
|
|
|
|
finally
|
|
|
|
FreeAndNil(info)
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TMainForm.SendCommand(const ACommand: string; AOnResponse: TProc<string>; AOnTimeout: TProc);
|
|
|
|
begin
|
|
|
|
if (not Assigned(ComPort)) or (not ComPort.Connected) then
|
|
|
|
exit;
|
|
|
|
|
|
|
|
StartCommand;
|
|
|
|
FOnResponse := AOnResponse;
|
|
|
|
FOnTimeout := AOnTimeout;
|
2017-09-10 15:10:41 +00:00
|
|
|
|
|
|
|
try
|
|
|
|
ComPort.WriteStr(ACommand + #10);
|
|
|
|
except
|
|
|
|
on E:Exception do
|
|
|
|
PortStatusLabel.Caption := 'Failed to send command';
|
|
|
|
end;
|
2017-09-10 13:15:32 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TMainForm.GetFanCount: Integer;
|
|
|
|
begin
|
|
|
|
Result := Length(FFanValues);
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TMainForm.GetReady: Boolean;
|
|
|
|
begin
|
|
|
|
Result := FReady;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TMainForm.GetIsRunning: Boolean;
|
|
|
|
begin
|
|
|
|
Result := Ready and (GetMaxValue > 0);
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TMainForm.GetValue(const AFan: Byte): Byte;
|
|
|
|
begin
|
|
|
|
if ValidFanIndex(AFan) then
|
|
|
|
Result := FFanValues[AFan]
|
|
|
|
else
|
|
|
|
Result := 0;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TMainForm.GetMaxValue: Byte;
|
|
|
|
var
|
|
|
|
value: Byte;
|
|
|
|
|
|
|
|
begin
|
|
|
|
Result := 0;
|
|
|
|
|
|
|
|
for value in FFanValues do
|
|
|
|
if value > Result then
|
|
|
|
Result := value;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TMainForm.SetValue(const AFan, AValue: Byte);
|
|
|
|
var
|
|
|
|
values: TStringBuilder;
|
|
|
|
value: Byte;
|
|
|
|
|
|
|
|
begin
|
2017-09-10 15:10:41 +00:00
|
|
|
if not Ready then
|
|
|
|
exit;
|
|
|
|
|
2017-09-10 13:15:32 +00:00
|
|
|
if ValidFanIndex(AFan) and (FFanValues[AFan] <> AValue) then
|
|
|
|
begin
|
|
|
|
FFanValues[AFan] := AValue;
|
|
|
|
|
|
|
|
values := TStringBuilder.Create;
|
|
|
|
try
|
|
|
|
for value in FFanValues do
|
|
|
|
begin
|
|
|
|
if values.Length > 0 then
|
|
|
|
values.Append(',');
|
|
|
|
|
|
|
|
values.Append(value);
|
|
|
|
end;
|
|
|
|
|
|
|
|
SendCommand('>SetFans:' + values.ToString, nil);
|
|
|
|
finally
|
|
|
|
FreeAndNil(values);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TMainForm.SetAll(const AValue: Byte);
|
|
|
|
var
|
|
|
|
fanIndex: Integer;
|
2017-09-10 15:10:41 +00:00
|
|
|
changed: Boolean;
|
2017-09-10 13:15:32 +00:00
|
|
|
|
|
|
|
begin
|
2017-09-10 15:10:41 +00:00
|
|
|
if not Ready then
|
|
|
|
exit;
|
|
|
|
|
|
|
|
changed := False;
|
|
|
|
|
2017-09-10 13:15:32 +00:00
|
|
|
for fanIndex := 0 to High(FFanValues) do
|
2017-09-10 15:10:41 +00:00
|
|
|
begin
|
|
|
|
if FFanValues[fanIndex] <> AValue then
|
|
|
|
begin
|
|
|
|
FFanValues[fanIndex] := AValue;
|
|
|
|
changed := True;
|
|
|
|
end;
|
|
|
|
end;
|
2017-09-10 13:15:32 +00:00
|
|
|
|
2017-09-10 15:10:41 +00:00
|
|
|
if changed then
|
|
|
|
SendCommand('>SetFans:A,' + IntToStr(AValue), nil);
|
2017-09-10 13:15:32 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TMainForm.SetFull;
|
|
|
|
var
|
|
|
|
fanIndex: Integer;
|
|
|
|
|
|
|
|
begin
|
2017-09-10 15:10:41 +00:00
|
|
|
if not Ready then
|
|
|
|
exit;
|
|
|
|
|
2017-09-10 13:15:32 +00:00
|
|
|
for fanIndex := 0 to High(FFanValues) do
|
|
|
|
FFanValues[fanIndex] := 255;
|
|
|
|
|
|
|
|
SendCommand('>SetFans:M', nil);
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TMainForm.TryConnect;
|
|
|
|
var
|
|
|
|
newPort: string;
|
|
|
|
|
|
|
|
begin
|
|
|
|
if PortComboBox.ItemIndex = -1 then
|
|
|
|
exit;
|
|
|
|
|
|
|
|
ConnectTimer.Enabled := False;
|
|
|
|
FReady := False;
|
|
|
|
CalibrationButton.Enabled := False;
|
|
|
|
|
|
|
|
PortStatusLabel.Caption := 'Attempting to connect';
|
|
|
|
PortStatusLabel.Update;
|
|
|
|
|
|
|
|
if not Assigned(FComPort) then
|
|
|
|
begin
|
|
|
|
FComPort := TComPort.Create(Self);
|
|
|
|
FComPort.BaudRate := br19200;
|
|
|
|
FComPort.OnRxChar := OnReceiveChar;
|
|
|
|
end;
|
|
|
|
|
|
|
|
newPort := PortComboBox.Items[PortComboBox.ItemIndex];
|
|
|
|
if ComPort.Connected and (newPort <> ComPort.Port) then
|
|
|
|
ComPort.Close;
|
|
|
|
|
|
|
|
ComPort.Port := newPort;
|
|
|
|
try
|
|
|
|
ComPort.Open;
|
|
|
|
|
|
|
|
SendCommand('>Info',
|
|
|
|
procedure(Response: string)
|
|
|
|
begin
|
|
|
|
if AnsiStartsText('<Info:', Response) then
|
|
|
|
begin
|
|
|
|
PortStatusLabel.Caption := 'Connected';
|
|
|
|
InitDevice(Copy(Response, 7, MaxInt));
|
|
|
|
FReady := True;
|
|
|
|
end else
|
|
|
|
begin
|
|
|
|
PortStatusLabel.Caption := 'Invalid response';
|
|
|
|
ConnectTimer.Enabled := True;
|
|
|
|
end;
|
|
|
|
end,
|
|
|
|
|
|
|
|
procedure
|
|
|
|
begin
|
|
|
|
PortStatusLabel.Caption := 'No response';
|
|
|
|
ConnectTimer.Enabled := True;
|
|
|
|
end);
|
|
|
|
except
|
|
|
|
on E:Exception do
|
|
|
|
begin
|
|
|
|
PortStatusLabel.Caption := 'Failed to connect';
|
|
|
|
ConnectTimer.Enabled := True;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TMainForm.OnReceiveChar(Sender: TObject; Count: Integer);
|
|
|
|
var
|
|
|
|
data: string;
|
|
|
|
terminatorPos: Integer;
|
|
|
|
|
|
|
|
begin
|
|
|
|
(Sender as TComPort).ReadStr(data, Count);
|
|
|
|
FReceived := FReceived + data;
|
|
|
|
|
|
|
|
terminatorPos := AnsiPos(#10, FReceived);
|
|
|
|
if terminatorPos > 0 then
|
|
|
|
begin
|
|
|
|
// Since the protocol is quite synchronous, this is good enough for now
|
|
|
|
SetLength(FReceived, terminatorPos - 1);
|
|
|
|
if Assigned(FOnResponse) then
|
|
|
|
FOnResponse(FReceived);
|
|
|
|
|
|
|
|
EndCommand;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TMainForm.ValidFanIndex(AFan: Byte): Boolean;
|
|
|
|
begin
|
|
|
|
Result := AFan < Length(FFanValues);
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TMainForm.SelectSimulator;
|
|
|
|
var
|
|
|
|
registeredSimulator: TRegisteredSimulator;
|
|
|
|
|
|
|
|
begin
|
|
|
|
if Assigned(FSimulator) then
|
|
|
|
begin
|
|
|
|
FSimulator.Stop;
|
|
|
|
FSimulator := nil;
|
|
|
|
end;
|
|
|
|
|
|
|
|
if SimulatorComboBox.ItemIndex = -1 then
|
|
|
|
exit;
|
|
|
|
|
|
|
|
registeredSimulator := (SimulatorComboBox.Items.Objects[SimulatorComboBox.ItemIndex] as TRegisteredSimulator);
|
|
|
|
FSimulator := registeredSimulator.ConstructorFunc(Self);
|
|
|
|
|
|
|
|
Simulator.SetUIParent(SimulatorPanel);
|
|
|
|
Simulator.Start;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TMainForm.ConnectTimerTimer(Sender: TObject);
|
|
|
|
begin
|
|
|
|
TryConnect;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TMainForm.ResponseTimerTimer(Sender: TObject);
|
|
|
|
begin
|
|
|
|
if Assigned(FOnTimeout) then
|
|
|
|
FOnTimeout;
|
|
|
|
|
|
|
|
EndCommand;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TMainForm.StartCommand;
|
|
|
|
begin
|
|
|
|
FReceived := '';
|
|
|
|
ResponseTimer.Enabled := True;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TMainForm.EndCommand;
|
|
|
|
begin
|
|
|
|
FReceived := '';
|
|
|
|
FOnResponse := nil;
|
|
|
|
FOnTimeout := nil;
|
|
|
|
ResponseTimer.Enabled := False;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
end.
|