mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 20:37:33 +00:00
Add full CAT support (serial + TCP)
New units: CATEngine (Kenwood + Thetis ZZ* commands), CATSerial (up to 4 serial ports), CATTcp (multi-client TCP server on port 4992). MainForm wired with TCATContext callbacks reusing existing WebOn* handlers. SettingsForm gets a new CAT tab with 4 serial port groups and TCP section. Settings.pas extended with CAT config fields (per-device, JSON-persisted). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+2062
File diff suppressed because it is too large
Load Diff
+275
@@ -0,0 +1,275 @@
|
|||||||
|
unit CATSerial;
|
||||||
|
{
|
||||||
|
CATSerial.pas — CAT через последовательные порты (до 4 штук).
|
||||||
|
|
||||||
|
Каждый порт запускает отдельный поток-слушатель, который:
|
||||||
|
1. Читает байты из COM/ttyS порта
|
||||||
|
2. Накапливает их в буфер до символа ';'
|
||||||
|
3. Передаёт команду в TCATEngine.Parse()
|
||||||
|
4. Отправляет ответ обратно в порт
|
||||||
|
|
||||||
|
Платформы:
|
||||||
|
• Linux : /dev/ttyS0..3, /dev/ttyUSB0, etc.
|
||||||
|
• Windows: COM1..COM4 (имя порта вводится вручную)
|
||||||
|
|
||||||
|
Зависимости: CATEngine, Serial (FPC fcl-serial RTL unit)
|
||||||
|
}
|
||||||
|
{$IFDEF FPC}
|
||||||
|
{$MODE Delphi}
|
||||||
|
{$ENDIF}
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses
|
||||||
|
Classes, SysUtils, SyncObjs,
|
||||||
|
Serial, // FPC cross-platform serial unit
|
||||||
|
{$IFDEF MSWINDOWS}Windows,{$ENDIF}
|
||||||
|
CATEngine;
|
||||||
|
|
||||||
|
const
|
||||||
|
CAT_SERIAL_MAX_PORTS = 4;
|
||||||
|
CAT_SERIAL_BUF_SIZE = 256;
|
||||||
|
|
||||||
|
type
|
||||||
|
TCATSerialParity = (cspNone, cspOdd, cspEven);
|
||||||
|
|
||||||
|
TCATSerialConfig = record
|
||||||
|
Enabled: Boolean;
|
||||||
|
PortName: string; // e.g. '/dev/ttyS0' or 'COM1'
|
||||||
|
BaudRate: Integer; // 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200
|
||||||
|
DataBits: Integer; // 7 or 8
|
||||||
|
StopBits: Integer; // 1 or 2
|
||||||
|
Parity: TCATSerialParity;
|
||||||
|
end;
|
||||||
|
|
||||||
|
TCATSerialPort = class;
|
||||||
|
|
||||||
|
{ TCATSerialThread — per-port listener thread }
|
||||||
|
TCATSerialThread = class(TThread)
|
||||||
|
private
|
||||||
|
FOwner: TCATSerialPort;
|
||||||
|
FHandle: TSerialHandle;
|
||||||
|
FBuf: string;
|
||||||
|
procedure ProcessBuffer;
|
||||||
|
function TryOpenPort: Boolean;
|
||||||
|
protected
|
||||||
|
procedure Execute; override;
|
||||||
|
public
|
||||||
|
constructor Create(AOwner: TCATSerialPort);
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TCATSerialPort — one serial CAT port }
|
||||||
|
TCATSerialPort = class
|
||||||
|
private
|
||||||
|
FConfig: TCATSerialConfig;
|
||||||
|
FEngine: TCATEngine;
|
||||||
|
FThread: TCATSerialThread;
|
||||||
|
FHandle: TSerialHandle;
|
||||||
|
FLock: TCriticalSection;
|
||||||
|
FActive: Boolean;
|
||||||
|
public
|
||||||
|
constructor Create(AEngine: TCATEngine; const ACfg: TCATSerialConfig);
|
||||||
|
destructor Destroy; override;
|
||||||
|
procedure Start;
|
||||||
|
procedure Stop;
|
||||||
|
procedure SendStr(const S: string);
|
||||||
|
property Handle: TSerialHandle read FHandle write FHandle;
|
||||||
|
property Config: TCATSerialConfig read FConfig;
|
||||||
|
property Engine: TCATEngine read FEngine;
|
||||||
|
property Active: Boolean read FActive;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TCATSerialManager — manages up to 4 serial ports }
|
||||||
|
TCATSerialManager = class
|
||||||
|
private
|
||||||
|
FPorts: array[0..CAT_SERIAL_MAX_PORTS-1] of TCATSerialPort;
|
||||||
|
FEngine: TCATEngine;
|
||||||
|
public
|
||||||
|
constructor Create(AEngine: TCATEngine);
|
||||||
|
destructor Destroy; override;
|
||||||
|
procedure ApplyConfig(const Configs: array of TCATSerialConfig);
|
||||||
|
procedure StopAll;
|
||||||
|
function ActiveCount: Integer;
|
||||||
|
end;
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
{ ── TCATSerialThread ──────────────────────────────────────────────────────── }
|
||||||
|
|
||||||
|
constructor TCATSerialThread.Create(AOwner: TCATSerialPort);
|
||||||
|
begin
|
||||||
|
FOwner := AOwner;
|
||||||
|
FBuf := '';
|
||||||
|
FreeOnTerminate := False;
|
||||||
|
inherited Create(True);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TCATSerialThread.TryOpenPort: Boolean;
|
||||||
|
var
|
||||||
|
cfg: TCATSerialConfig;
|
||||||
|
h: TSerialHandle;
|
||||||
|
par: TParityType;
|
||||||
|
begin
|
||||||
|
Result := False;
|
||||||
|
cfg := FOwner.Config;
|
||||||
|
h := SerOpen(cfg.PortName);
|
||||||
|
{$IFDEF MSWINDOWS}
|
||||||
|
if h = TSerialHandle(INVALID_HANDLE_VALUE) then Exit;
|
||||||
|
{$ELSE}
|
||||||
|
if h < 0 then Exit;
|
||||||
|
{$ENDIF}
|
||||||
|
|
||||||
|
case cfg.Parity of
|
||||||
|
cspNone: par := NoneParity;
|
||||||
|
cspOdd: par := OddParity;
|
||||||
|
cspEven: par := EvenParity;
|
||||||
|
else par := NoneParity;
|
||||||
|
end;
|
||||||
|
|
||||||
|
SerSetParams(h, cfg.BaudRate, cfg.DataBits, par, cfg.StopBits, []);
|
||||||
|
FHandle := h;
|
||||||
|
FOwner.Handle := h;
|
||||||
|
Result := True;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TCATSerialThread.ProcessBuffer;
|
||||||
|
var
|
||||||
|
tpos: Integer;
|
||||||
|
cmd, resp: string;
|
||||||
|
begin
|
||||||
|
repeat
|
||||||
|
tpos := Pos(';', FBuf);
|
||||||
|
if tpos = 0 then Break;
|
||||||
|
cmd := Copy(FBuf, 1, tpos);
|
||||||
|
Delete(FBuf, 1, tpos);
|
||||||
|
resp := FOwner.Engine.Parse(cmd);
|
||||||
|
if resp <> '' then
|
||||||
|
FOwner.SendStr(resp);
|
||||||
|
until tpos = 0;
|
||||||
|
// Guard against runaway buffers (no terminator seen in 256+ chars)
|
||||||
|
if Length(FBuf) > CAT_SERIAL_BUF_SIZE then FBuf := '';
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TCATSerialThread.Execute;
|
||||||
|
var
|
||||||
|
ch: Byte;
|
||||||
|
n: LongInt;
|
||||||
|
begin
|
||||||
|
if not TryOpenPort then Exit;
|
||||||
|
try
|
||||||
|
while not Terminated do begin
|
||||||
|
n := SerReadTimeout(FHandle, ch, 50);
|
||||||
|
if n = 1 then begin
|
||||||
|
FBuf := FBuf + Chr(ch);
|
||||||
|
if ch = Ord(';') then ProcessBuffer;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
finally
|
||||||
|
SerClose(FHandle);
|
||||||
|
FOwner.Handle := -1;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ ── TCATSerialPort ────────────────────────────────────────────────────────── }
|
||||||
|
|
||||||
|
constructor TCATSerialPort.Create(AEngine: TCATEngine; const ACfg: TCATSerialConfig);
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
FEngine := AEngine;
|
||||||
|
FConfig := ACfg;
|
||||||
|
FHandle := -1;
|
||||||
|
FLock := TCriticalSection.Create;
|
||||||
|
FActive := False;
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TCATSerialPort.Destroy;
|
||||||
|
begin
|
||||||
|
Stop;
|
||||||
|
FLock.Free;
|
||||||
|
inherited;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TCATSerialPort.Start;
|
||||||
|
begin
|
||||||
|
if FActive or not FConfig.Enabled then Exit;
|
||||||
|
FActive := True;
|
||||||
|
FThread := TCATSerialThread.Create(Self);
|
||||||
|
FThread.Start;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TCATSerialPort.Stop;
|
||||||
|
begin
|
||||||
|
if not FActive then Exit;
|
||||||
|
FActive := False;
|
||||||
|
if Assigned(FThread) then begin
|
||||||
|
FThread.Terminate;
|
||||||
|
FThread.WaitFor;
|
||||||
|
FreeAndNil(FThread);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TCATSerialPort.SendStr(const S: string);
|
||||||
|
var buf: AnsiString;
|
||||||
|
begin
|
||||||
|
if FHandle < 0 then Exit;
|
||||||
|
FLock.Acquire;
|
||||||
|
try
|
||||||
|
buf := AnsiString(S);
|
||||||
|
SerWrite(FHandle, buf[1], Length(buf));
|
||||||
|
except
|
||||||
|
// ignore write errors (port may have been closed)
|
||||||
|
end;
|
||||||
|
FLock.Release;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ ── TCATSerialManager ─────────────────────────────────────────────────────── }
|
||||||
|
|
||||||
|
constructor TCATSerialManager.Create(AEngine: TCATEngine);
|
||||||
|
var i: Integer;
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
FEngine := AEngine;
|
||||||
|
for i := 0 to CAT_SERIAL_MAX_PORTS-1 do
|
||||||
|
FPorts[i] := nil;
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TCATSerialManager.Destroy;
|
||||||
|
begin
|
||||||
|
StopAll;
|
||||||
|
inherited;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TCATSerialManager.ApplyConfig(const Configs: array of TCATSerialConfig);
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
|
cnt: Integer;
|
||||||
|
begin
|
||||||
|
StopAll;
|
||||||
|
cnt := Length(Configs);
|
||||||
|
if cnt > CAT_SERIAL_MAX_PORTS then cnt := CAT_SERIAL_MAX_PORTS;
|
||||||
|
for i := 0 to cnt-1 do begin
|
||||||
|
FPorts[i] := TCATSerialPort.Create(FEngine, Configs[i]);
|
||||||
|
if Configs[i].Enabled then FPorts[i].Start;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TCATSerialManager.StopAll;
|
||||||
|
var i: Integer;
|
||||||
|
begin
|
||||||
|
for i := 0 to CAT_SERIAL_MAX_PORTS-1 do begin
|
||||||
|
if Assigned(FPorts[i]) then begin
|
||||||
|
FPorts[i].Stop;
|
||||||
|
FreeAndNil(FPorts[i]);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TCATSerialManager.ActiveCount: Integer;
|
||||||
|
var i: Integer;
|
||||||
|
begin
|
||||||
|
Result := 0;
|
||||||
|
for i := 0 to CAT_SERIAL_MAX_PORTS-1 do
|
||||||
|
if Assigned(FPorts[i]) and FPorts[i].Active then Inc(Result);
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
+490
@@ -0,0 +1,490 @@
|
|||||||
|
unit CATTcp;
|
||||||
|
{
|
||||||
|
CATTcp.pas — TCP CAT сервер (аналог TCPIPcatServer.cs из Thetis).
|
||||||
|
|
||||||
|
Архитектура:
|
||||||
|
• Один listening socket (TCATTcpServer)
|
||||||
|
• На каждое входящее соединение — отдельный поток (TCATTcpClientThread)
|
||||||
|
• Команды вида "ZZFA00014250000;" обрабатываются через TCATEngine.Parse()
|
||||||
|
• Поддержка ZZGA/ZZGR для идентификации клиентов (directed broadcast)
|
||||||
|
• Welcome-строка при подключении
|
||||||
|
|
||||||
|
Порт по умолчанию: 4992
|
||||||
|
|
||||||
|
Платформы: Linux / Windows (через WebUtils.SockClose/SockShutdown).
|
||||||
|
}
|
||||||
|
{$IFDEF FPC}
|
||||||
|
{$MODE Delphi}
|
||||||
|
{$ENDIF}
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses
|
||||||
|
Classes, SysUtils, SyncObjs,
|
||||||
|
WebUtils, // SockClose, SockShutdown, SOCK_INVALID
|
||||||
|
{$IFDEF MSWINDOWS}
|
||||||
|
Windows, WinSock2
|
||||||
|
{$ELSE}
|
||||||
|
BaseUnix, Sockets
|
||||||
|
{$ENDIF},
|
||||||
|
CATEngine;
|
||||||
|
|
||||||
|
const
|
||||||
|
CAT_TCP_DEFAULT_PORT = 4992;
|
||||||
|
CAT_TCP_BUF_SIZE = 4096;
|
||||||
|
CAT_TCP_WELCOME = '#EWSDR CAT TCP Server;';
|
||||||
|
|
||||||
|
type
|
||||||
|
TCATTcpServer = class;
|
||||||
|
|
||||||
|
{ TCATTcpClientThread — один поток на TCP клиента }
|
||||||
|
TCATTcpClientThread = class(TThread)
|
||||||
|
private
|
||||||
|
FServer: TCATTcpServer;
|
||||||
|
FSocket: TSocket;
|
||||||
|
FBuf: string;
|
||||||
|
FClientIds: TStringList;
|
||||||
|
FIdLock: TCriticalSection;
|
||||||
|
FMarkDelete: Boolean;
|
||||||
|
|
||||||
|
procedure ProcessBuffer;
|
||||||
|
procedure SendStr(const S: string);
|
||||||
|
procedure HandleCmd(const Cmd: string);
|
||||||
|
procedure AddClientId(const Id: string);
|
||||||
|
procedure RemoveClientId(const Id: string);
|
||||||
|
protected
|
||||||
|
procedure Execute; override;
|
||||||
|
public
|
||||||
|
constructor Create(AServer: TCATTcpServer; ASocket: TSocket);
|
||||||
|
destructor Destroy; override;
|
||||||
|
procedure SendData(const Msg: string; const IdLimit: TStringList = nil);
|
||||||
|
function IsMarkedForDelete: Boolean;
|
||||||
|
procedure Disconnect;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TCATTcpServer }
|
||||||
|
TCATTcpServer = class
|
||||||
|
private
|
||||||
|
FEngine: TCATEngine;
|
||||||
|
FPort: Integer;
|
||||||
|
FListenSock: TSocket;
|
||||||
|
FServerThread: TThread;
|
||||||
|
FPurgeThread: TThread;
|
||||||
|
FClients: TList;
|
||||||
|
FClientsLock: TCriticalSection;
|
||||||
|
FRunning: Boolean;
|
||||||
|
FSendWelcome: Boolean;
|
||||||
|
FLastError: string;
|
||||||
|
|
||||||
|
procedure ServerLoop;
|
||||||
|
procedure PurgeLoop;
|
||||||
|
function InitListen: Boolean;
|
||||||
|
public
|
||||||
|
constructor Create(AEngine: TCATEngine; APort: Integer = CAT_TCP_DEFAULT_PORT);
|
||||||
|
destructor Destroy; override;
|
||||||
|
procedure Start;
|
||||||
|
procedure Stop;
|
||||||
|
procedure Broadcast(const Msg: string; const IdLimit: TStringList = nil);
|
||||||
|
function ClientCount: Integer;
|
||||||
|
property Port: Integer read FPort write FPort;
|
||||||
|
property SendWelcome: Boolean read FSendWelcome write FSendWelcome;
|
||||||
|
property Running: Boolean read FRunning;
|
||||||
|
property LastError: string read FLastError;
|
||||||
|
end;
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
{ ── internal thread wrappers ─────────────────────────────────────────────── }
|
||||||
|
|
||||||
|
type
|
||||||
|
TServerThread = class(TThread)
|
||||||
|
private FServer: TCATTcpServer;
|
||||||
|
protected procedure Execute; override;
|
||||||
|
public constructor Create(S: TCATTcpServer);
|
||||||
|
end;
|
||||||
|
TPurgeThread = class(TThread)
|
||||||
|
private FServer: TCATTcpServer;
|
||||||
|
protected procedure Execute; override;
|
||||||
|
public constructor Create(S: TCATTcpServer);
|
||||||
|
end;
|
||||||
|
|
||||||
|
constructor TServerThread.Create(S: TCATTcpServer);
|
||||||
|
begin FServer := S; FreeOnTerminate := False; inherited Create(True); end;
|
||||||
|
procedure TServerThread.Execute; begin FServer.ServerLoop; end;
|
||||||
|
|
||||||
|
constructor TPurgeThread.Create(S: TCATTcpServer);
|
||||||
|
begin FServer := S; FreeOnTerminate := False; inherited Create(True); end;
|
||||||
|
procedure TPurgeThread.Execute; begin FServer.PurgeLoop; end;
|
||||||
|
|
||||||
|
{ ── TCATTcpClientThread ───────────────────────────────────────────────────── }
|
||||||
|
|
||||||
|
constructor TCATTcpClientThread.Create(AServer: TCATTcpServer; ASocket: TSocket);
|
||||||
|
begin
|
||||||
|
FServer := AServer;
|
||||||
|
FSocket := ASocket;
|
||||||
|
FBuf := '';
|
||||||
|
FMarkDelete := False;
|
||||||
|
FClientIds := TStringList.Create;
|
||||||
|
FIdLock := TCriticalSection.Create;
|
||||||
|
FreeOnTerminate := False;
|
||||||
|
inherited Create(True);
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TCATTcpClientThread.Destroy;
|
||||||
|
begin
|
||||||
|
FClientIds.Free;
|
||||||
|
FIdLock.Free;
|
||||||
|
inherited;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TCATTcpClientThread.AddClientId(const Id: string);
|
||||||
|
var lo: string;
|
||||||
|
begin
|
||||||
|
lo := LowerCase(Id);
|
||||||
|
FIdLock.Acquire;
|
||||||
|
try
|
||||||
|
if FClientIds.IndexOf(lo) < 0 then FClientIds.Add(lo);
|
||||||
|
finally FIdLock.Release; end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TCATTcpClientThread.RemoveClientId(const Id: string);
|
||||||
|
var lo: string; idx: Integer;
|
||||||
|
begin
|
||||||
|
lo := LowerCase(Id);
|
||||||
|
FIdLock.Acquire;
|
||||||
|
try
|
||||||
|
idx := FClientIds.IndexOf(lo);
|
||||||
|
if idx >= 0 then FClientIds.Delete(idx);
|
||||||
|
finally FIdLock.Release; end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TCATTcpClientThread.SendStr(const S: string);
|
||||||
|
var
|
||||||
|
buf: AnsiString;
|
||||||
|
n: Integer;
|
||||||
|
begin
|
||||||
|
if FSocket = SOCK_INVALID then Exit;
|
||||||
|
buf := AnsiString(S);
|
||||||
|
if Length(buf) = 0 then Exit;
|
||||||
|
{$IFDEF MSWINDOWS}
|
||||||
|
n := send(FSocket, buf[1], Length(buf), 0);
|
||||||
|
if n = SOCKET_ERROR then begin
|
||||||
|
{$ELSE}
|
||||||
|
n := fpSend(FSocket, @buf[1], Length(buf), 0);
|
||||||
|
if n <= 0 then begin
|
||||||
|
{$ENDIF}
|
||||||
|
FMarkDelete := True;
|
||||||
|
Terminate;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TCATTcpClientThread.HandleCmd(const Cmd: string);
|
||||||
|
const
|
||||||
|
ZZGA_PFX = 'ZZGA';
|
||||||
|
ZZGR_PFX = 'ZZGR';
|
||||||
|
var
|
||||||
|
cmd2, guid, resp: string;
|
||||||
|
begin
|
||||||
|
cmd2 := Trim(Cmd);
|
||||||
|
if cmd2 = '' then Exit;
|
||||||
|
if (Length(cmd2) >= 4) and (UpperCase(Copy(cmd2, 1, 4)) = ZZGA_PFX) then begin
|
||||||
|
if Length(cmd2) >= 40 then begin // 4 + 36
|
||||||
|
guid := LowerCase(Copy(cmd2, 5, 36));
|
||||||
|
AddClientId(guid);
|
||||||
|
SendStr(ZZGA_PFX + guid + ';');
|
||||||
|
end else
|
||||||
|
SendStr('?;');
|
||||||
|
end else if (Length(cmd2) >= 4) and (UpperCase(Copy(cmd2, 1, 4)) = ZZGR_PFX) then begin
|
||||||
|
if Length(cmd2) >= 40 then begin
|
||||||
|
guid := LowerCase(Copy(cmd2, 5, 36));
|
||||||
|
RemoveClientId(guid);
|
||||||
|
SendStr(ZZGR_PFX + guid + ';');
|
||||||
|
end else
|
||||||
|
SendStr('?;');
|
||||||
|
end else begin
|
||||||
|
resp := FServer.FEngine.Parse(cmd2);
|
||||||
|
if resp <> '' then SendStr(resp);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TCATTcpClientThread.ProcessBuffer;
|
||||||
|
var tpos: Integer; msg: string;
|
||||||
|
begin
|
||||||
|
repeat
|
||||||
|
tpos := Pos(';', FBuf);
|
||||||
|
if tpos = 0 then Break;
|
||||||
|
msg := Copy(FBuf, 1, tpos);
|
||||||
|
Delete(FBuf, 1, tpos);
|
||||||
|
HandleCmd(msg);
|
||||||
|
until tpos = 0;
|
||||||
|
if Length(FBuf) > 1024 then FBuf := '';
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TCATTcpClientThread.Execute;
|
||||||
|
var
|
||||||
|
rawbuf: array[0..CAT_TCP_BUF_SIZE-1] of Byte;
|
||||||
|
n: Integer;
|
||||||
|
chunk: AnsiString;
|
||||||
|
begin
|
||||||
|
if FServer.SendWelcome then
|
||||||
|
SendStr(CAT_TCP_WELCOME);
|
||||||
|
|
||||||
|
while not Terminated do begin
|
||||||
|
{$IFDEF MSWINDOWS}
|
||||||
|
n := recv(FSocket, rawbuf[0], CAT_TCP_BUF_SIZE, 0);
|
||||||
|
if n = SOCKET_ERROR then begin FMarkDelete := True; Break; end;
|
||||||
|
{$ELSE}
|
||||||
|
n := fpRecv(FSocket, @rawbuf[0], CAT_TCP_BUF_SIZE, 0);
|
||||||
|
if n <= 0 then begin FMarkDelete := True; Break; end;
|
||||||
|
{$ENDIF}
|
||||||
|
SetLength(chunk, n);
|
||||||
|
Move(rawbuf[0], chunk[1], n);
|
||||||
|
FBuf := FBuf + string(chunk);
|
||||||
|
ProcessBuffer;
|
||||||
|
end;
|
||||||
|
|
||||||
|
SockShutdown(FSocket);
|
||||||
|
SockClose(FSocket);
|
||||||
|
FSocket := SOCK_INVALID;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TCATTcpClientThread.SendData(const Msg: string; const IdLimit: TStringList);
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
|
send: Boolean;
|
||||||
|
begin
|
||||||
|
if IdLimit = nil then begin
|
||||||
|
SendStr(Msg);
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
send := False;
|
||||||
|
FIdLock.Acquire;
|
||||||
|
try
|
||||||
|
for i := 0 to FClientIds.Count-1 do
|
||||||
|
if IdLimit.IndexOf(FClientIds[i]) >= 0 then begin
|
||||||
|
send := True;
|
||||||
|
Break;
|
||||||
|
end;
|
||||||
|
finally FIdLock.Release; end;
|
||||||
|
if send then SendStr(Msg);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TCATTcpClientThread.IsMarkedForDelete: Boolean;
|
||||||
|
begin Result := FMarkDelete; end;
|
||||||
|
|
||||||
|
procedure TCATTcpClientThread.Disconnect;
|
||||||
|
begin
|
||||||
|
Terminate;
|
||||||
|
SockShutdown(FSocket);
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ ── TCATTcpServer ─────────────────────────────────────────────────────────── }
|
||||||
|
|
||||||
|
constructor TCATTcpServer.Create(AEngine: TCATEngine; APort: Integer);
|
||||||
|
{$IFDEF MSWINDOWS}
|
||||||
|
var wsa: TWSAData;
|
||||||
|
{$ENDIF}
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
FEngine := AEngine;
|
||||||
|
FPort := APort;
|
||||||
|
FListenSock := SOCK_INVALID;
|
||||||
|
FClients := TList.Create;
|
||||||
|
FClientsLock := TCriticalSection.Create;
|
||||||
|
FRunning := False;
|
||||||
|
FSendWelcome := True;
|
||||||
|
FLastError := '';
|
||||||
|
{$IFDEF MSWINDOWS}
|
||||||
|
WSAStartup($0202, wsa);
|
||||||
|
{$ENDIF}
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TCATTcpServer.Destroy;
|
||||||
|
begin
|
||||||
|
Stop;
|
||||||
|
FClients.Free;
|
||||||
|
FClientsLock.Free;
|
||||||
|
{$IFDEF MSWINDOWS}
|
||||||
|
WSACleanup;
|
||||||
|
{$ENDIF}
|
||||||
|
inherited;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TCATTcpServer.InitListen: Boolean;
|
||||||
|
var
|
||||||
|
Addr: {$IFDEF MSWINDOWS}TSockAddrIn{$ELSE}TInetSockAddr{$ENDIF};
|
||||||
|
One: Integer;
|
||||||
|
begin
|
||||||
|
Result := False;
|
||||||
|
{$IFDEF MSWINDOWS}
|
||||||
|
FListenSock := socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||||
|
{$ELSE}
|
||||||
|
FListenSock := fpSocket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||||
|
{$ENDIF}
|
||||||
|
if FListenSock = SOCK_INVALID then begin
|
||||||
|
FLastError := 'Cannot create socket';
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
One := 1;
|
||||||
|
FillChar(Addr, SizeOf(Addr), 0);
|
||||||
|
Addr.sin_family := AF_INET;
|
||||||
|
Addr.sin_port := htons(FPort);
|
||||||
|
|
||||||
|
{$IFDEF MSWINDOWS}
|
||||||
|
setsockopt(FListenSock, SOL_SOCKET, SO_REUSEADDR, PChar(@One), SizeOf(One));
|
||||||
|
Addr.sin_addr.S_addr := INADDR_ANY;
|
||||||
|
if bind(FListenSock, @Addr, SizeOf(Addr)) = SOCKET_ERROR then begin
|
||||||
|
FLastError := 'bind failed';
|
||||||
|
SockClose(FListenSock); FListenSock := SOCK_INVALID; Exit;
|
||||||
|
end;
|
||||||
|
if listen(FListenSock, 8) = SOCKET_ERROR then begin
|
||||||
|
FLastError := 'listen failed';
|
||||||
|
SockClose(FListenSock); FListenSock := SOCK_INVALID; Exit;
|
||||||
|
end;
|
||||||
|
{$ELSE}
|
||||||
|
fpSetSockOpt(FListenSock, SOL_SOCKET, SO_REUSEADDR, @One, SizeOf(One));
|
||||||
|
Addr.sin_addr.s_addr := htonl(INADDR_ANY);
|
||||||
|
if fpBind(FListenSock, @Addr, SizeOf(Addr)) <> 0 then begin
|
||||||
|
FLastError := 'bind failed on port ' + IntToStr(FPort);
|
||||||
|
SockClose(FListenSock); FListenSock := SOCK_INVALID; Exit;
|
||||||
|
end;
|
||||||
|
if fpListen(FListenSock, 8) <> 0 then begin
|
||||||
|
FLastError := 'listen failed';
|
||||||
|
SockClose(FListenSock); FListenSock := SOCK_INVALID; Exit;
|
||||||
|
end;
|
||||||
|
{$ENDIF}
|
||||||
|
Result := True;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TCATTcpServer.Start;
|
||||||
|
begin
|
||||||
|
if FRunning then Exit;
|
||||||
|
if not InitListen then Exit;
|
||||||
|
FRunning := True;
|
||||||
|
FServerThread := TServerThread.Create(Self);
|
||||||
|
TServerThread(FServerThread).Start;
|
||||||
|
FPurgeThread := TPurgeThread.Create(Self);
|
||||||
|
TPurgeThread(FPurgeThread).Start;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TCATTcpServer.Stop;
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
|
cli: TCATTcpClientThread;
|
||||||
|
begin
|
||||||
|
if not FRunning then Exit;
|
||||||
|
FRunning := False;
|
||||||
|
|
||||||
|
if FListenSock <> SOCK_INVALID then begin
|
||||||
|
SockShutdown(FListenSock);
|
||||||
|
SockClose(FListenSock);
|
||||||
|
FListenSock := SOCK_INVALID;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if Assigned(FServerThread) then begin
|
||||||
|
FServerThread.Terminate;
|
||||||
|
FServerThread.WaitFor;
|
||||||
|
FreeAndNil(FServerThread);
|
||||||
|
end;
|
||||||
|
if Assigned(FPurgeThread) then begin
|
||||||
|
FPurgeThread.Terminate;
|
||||||
|
FPurgeThread.WaitFor;
|
||||||
|
FreeAndNil(FPurgeThread);
|
||||||
|
end;
|
||||||
|
|
||||||
|
FClientsLock.Acquire;
|
||||||
|
try
|
||||||
|
for i := 0 to FClients.Count-1 do begin
|
||||||
|
cli := TCATTcpClientThread(FClients[i]);
|
||||||
|
cli.Disconnect;
|
||||||
|
cli.WaitFor;
|
||||||
|
cli.Free;
|
||||||
|
end;
|
||||||
|
FClients.Clear;
|
||||||
|
finally FClientsLock.Release; end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TCATTcpServer.ServerLoop;
|
||||||
|
var
|
||||||
|
cli: TCATTcpClientThread;
|
||||||
|
clientSock: TSocket;
|
||||||
|
clientAddr: {$IFDEF MSWINDOWS}TSockAddrIn{$ELSE}TInetSockAddr{$ENDIF};
|
||||||
|
addrLen: {$IFDEF MSWINDOWS}Integer{$ELSE}TSockLen{$ENDIF};
|
||||||
|
begin
|
||||||
|
addrLen := SizeOf(clientAddr);
|
||||||
|
while FRunning do begin
|
||||||
|
{$IFDEF MSWINDOWS}
|
||||||
|
clientSock := accept(FListenSock, @clientAddr, @addrLen);
|
||||||
|
if clientSock = INVALID_SOCKET then Break;
|
||||||
|
{$ELSE}
|
||||||
|
clientSock := fpAccept(FListenSock, @clientAddr, @addrLen);
|
||||||
|
if clientSock = SOCK_INVALID then Break;
|
||||||
|
{$ENDIF}
|
||||||
|
if not FRunning then begin SockClose(clientSock); Break; end;
|
||||||
|
|
||||||
|
cli := TCATTcpClientThread.Create(Self, clientSock);
|
||||||
|
FClientsLock.Acquire;
|
||||||
|
try FClients.Add(cli);
|
||||||
|
finally FClientsLock.Release; end;
|
||||||
|
cli.Start;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TCATTcpServer.PurgeLoop;
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
|
cli: TCATTcpClientThread;
|
||||||
|
del: TList;
|
||||||
|
begin
|
||||||
|
del := TList.Create;
|
||||||
|
try
|
||||||
|
while FRunning do begin
|
||||||
|
Sleep(5000);
|
||||||
|
del.Clear;
|
||||||
|
FClientsLock.Acquire;
|
||||||
|
try
|
||||||
|
for i := FClients.Count-1 downto 0 do begin
|
||||||
|
cli := TCATTcpClientThread(FClients[i]);
|
||||||
|
if cli.IsMarkedForDelete then begin
|
||||||
|
del.Add(cli);
|
||||||
|
FClients.Delete(i);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
finally FClientsLock.Release; end;
|
||||||
|
for i := 0 to del.Count-1 do begin
|
||||||
|
cli := TCATTcpClientThread(del[i]);
|
||||||
|
cli.WaitFor;
|
||||||
|
cli.Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
finally del.Free; end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TCATTcpServer.Broadcast(const Msg: string; const IdLimit: TStringList);
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
|
cli: TCATTcpClientThread;
|
||||||
|
begin
|
||||||
|
FClientsLock.Acquire;
|
||||||
|
try
|
||||||
|
for i := 0 to FClients.Count-1 do begin
|
||||||
|
cli := TCATTcpClientThread(FClients[i]);
|
||||||
|
if not cli.IsMarkedForDelete then
|
||||||
|
cli.SendData(Msg, IdLimit);
|
||||||
|
end;
|
||||||
|
finally FClientsLock.Release; end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TCATTcpServer.ClientCount: Integer;
|
||||||
|
var i: Integer;
|
||||||
|
begin
|
||||||
|
Result := 0;
|
||||||
|
FClientsLock.Acquire;
|
||||||
|
try
|
||||||
|
for i := 0 to FClients.Count-1 do
|
||||||
|
if not TCATTcpClientThread(FClients[i]).IsMarkedForDelete then Inc(Result);
|
||||||
|
finally FClientsLock.Release; end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
+308
-1
@@ -29,7 +29,8 @@ uses
|
|||||||
WDSP, WDSPEngine, AudioOutput, AudioInput,
|
WDSP, WDSPEngine, AudioOutput, AudioInput,
|
||||||
IntfGraphics, FPImage,
|
IntfGraphics, FPImage,
|
||||||
Settings,
|
Settings,
|
||||||
WebServer;
|
WebServer,
|
||||||
|
CATEngine, CATSerial, CATTcp;
|
||||||
|
|
||||||
const
|
const
|
||||||
CLR_BG = TColor($00101010);
|
CLR_BG = TColor($00101010);
|
||||||
@@ -286,6 +287,12 @@ type
|
|||||||
// --- Settings ---
|
// --- Settings ---
|
||||||
FSettings: TSettingsManager;
|
FSettings: TSettingsManager;
|
||||||
FWebServer: TWebServer; // веб-интерфейс (порт 8080)
|
FWebServer: TWebServer; // веб-интерфейс (порт 8080)
|
||||||
|
// --- CAT ---
|
||||||
|
FCATEngine: TCATEngine;
|
||||||
|
FCATSerial: TCATSerialManager;
|
||||||
|
FCATTcp: TCATTcpServer;
|
||||||
|
FCATSyncFreq: Double;
|
||||||
|
FCATLastGlobal: TGlobalSettings; // текущие CAT-настройки (для сохранения)
|
||||||
// Временные поля для передачи параметров в Synchronize-методы
|
// Временные поля для передачи параметров в Synchronize-методы
|
||||||
FWebSyncFreq: Double;
|
FWebSyncFreq: Double;
|
||||||
FWebSyncInt: Integer;
|
FWebSyncInt: Integer;
|
||||||
@@ -532,6 +539,45 @@ type
|
|||||||
procedure SyncWebCenter;
|
procedure SyncWebCenter;
|
||||||
procedure SyncWebMOX;
|
procedure SyncWebMOX;
|
||||||
procedure SyncWebDrive;
|
procedure SyncWebDrive;
|
||||||
|
// --- CAT callbacks -------------------------------------------------------
|
||||||
|
function CATGetVfoA: Double;
|
||||||
|
function CATGetVfoB: Double;
|
||||||
|
function CATGetMode: Integer;
|
||||||
|
function CATGetActiveVfo: Integer;
|
||||||
|
function CATGetAGCMode: Integer;
|
||||||
|
function CATGetVolume: Integer;
|
||||||
|
function CATGetDriveLevel: Integer;
|
||||||
|
function CATGetFilterIdx: Integer;
|
||||||
|
function CATGetFilterBW: Integer;
|
||||||
|
function CATGetNRMode: Integer;
|
||||||
|
function CATGetNBMode: Integer;
|
||||||
|
function CATGetSNB: Boolean;
|
||||||
|
function CATGetANF: Boolean;
|
||||||
|
function CATGetTX: Boolean;
|
||||||
|
function CATGetRunning: Boolean;
|
||||||
|
function CATGetSMeter: Double;
|
||||||
|
function CATGetBand: Integer;
|
||||||
|
procedure CATSetVfoA(V: Double);
|
||||||
|
procedure CATSetVfoB(V: Double);
|
||||||
|
procedure CATSetFilterIdx(V: Integer);
|
||||||
|
procedure CATDoBandUp;
|
||||||
|
procedure CATDoBandDown;
|
||||||
|
procedure CATDoTuneUp;
|
||||||
|
procedure CATDoTuneDown;
|
||||||
|
procedure SyncCATVfoA;
|
||||||
|
procedure SyncCATVfoB;
|
||||||
|
procedure SyncCATBandUp;
|
||||||
|
procedure SyncCATBandDown;
|
||||||
|
procedure SyncCATTuneUp;
|
||||||
|
procedure SyncCATTuneDown;
|
||||||
|
procedure InitCATEngine;
|
||||||
|
procedure CATApplySettings(const G: TGlobalSettings);
|
||||||
|
procedure OnCATSettingsChange(
|
||||||
|
const SerEnabled: array of Boolean;
|
||||||
|
const SerPort: array of string;
|
||||||
|
const SerBaud, SerDataBits, SerStopBits, SerParity: array of Integer;
|
||||||
|
TcpEnabled: Boolean; TcpPort: Integer);
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
procedure BtnWfAGCClick(Sender: TObject);
|
procedure BtnWfAGCClick(Sender: TObject);
|
||||||
procedure BtnWfNFClick(Sender: TObject);
|
procedure BtnWfNFClick(Sender: TObject);
|
||||||
procedure BtnHidePanelClick(Sender: TObject);
|
procedure BtnHidePanelClick(Sender: TObject);
|
||||||
@@ -974,6 +1020,15 @@ begin
|
|||||||
// Audio device names
|
// Audio device names
|
||||||
Result.AudioOutDevice := FAudioOutDevName;
|
Result.AudioOutDevice := FAudioOutDevName;
|
||||||
Result.AudioInDevice := FAudioInDevName;
|
Result.AudioInDevice := FAudioInDevName;
|
||||||
|
// CAT settings — carried from the last loaded/saved device config
|
||||||
|
Result.CATSerialEnabled := FCATLastGlobal.CATSerialEnabled;
|
||||||
|
Result.CATSerialPort := FCATLastGlobal.CATSerialPort;
|
||||||
|
Result.CATSerialBaud := FCATLastGlobal.CATSerialBaud;
|
||||||
|
Result.CATSerialDataBits := FCATLastGlobal.CATSerialDataBits;
|
||||||
|
Result.CATSerialStopBits := FCATLastGlobal.CATSerialStopBits;
|
||||||
|
Result.CATSerialParity := FCATLastGlobal.CATSerialParity;
|
||||||
|
Result.CATTcpEnabled := FCATLastGlobal.CATTcpEnabled;
|
||||||
|
Result.CATTcpPort := FCATLastGlobal.CATTcpPort;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMainForm.SaveCurrentBand;
|
procedure TMainForm.SaveCurrentBand;
|
||||||
@@ -1159,6 +1214,8 @@ begin
|
|||||||
FWebServer.OnMOX := WebOnMOX;
|
FWebServer.OnMOX := WebOnMOX;
|
||||||
FWebServer.OnDrive := WebOnDrive;
|
FWebServer.OnDrive := WebOnDrive;
|
||||||
FWebServer.Start;
|
FWebServer.Start;
|
||||||
|
FillChar(FCATLastGlobal, SizeOf(FCATLastGlobal), 0);
|
||||||
|
InitCATEngine;
|
||||||
FSMeterPeak := -130;
|
FSMeterPeak := -130;
|
||||||
FSMeterMin := -130;
|
FSMeterMin := -130;
|
||||||
FSMeterAvg := -130;
|
FSMeterAvg := -130;
|
||||||
@@ -1298,6 +1355,9 @@ begin
|
|||||||
FSettings.Free;
|
FSettings.Free;
|
||||||
FWebServer.Stop;
|
FWebServer.Stop;
|
||||||
FWebServer.Free;
|
FWebServer.Free;
|
||||||
|
if Assigned(FCATTcp) then begin FCATTcp.Stop; FreeAndNil(FCATTcp); end;
|
||||||
|
if Assigned(FCATSerial) then begin FCATSerial.StopAll; FreeAndNil(FCATSerial); end;
|
||||||
|
FreeAndNil(FCATEngine);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMainForm.FormClose(Sender: TObject; var CloseAction: TCloseAction);
|
procedure TMainForm.FormClose(Sender: TObject; var CloseAction: TCloseAction);
|
||||||
@@ -3909,6 +3969,8 @@ begin
|
|||||||
Move(FNetwork.Device.MAC[0], FDevMAC[0], 6);
|
Move(FNetwork.Device.MAC[0], FDevMAC[0], 6);
|
||||||
FDevConnected := True;
|
FDevConnected := True;
|
||||||
FSettings.LoadDevice(FDevMAC, G_Settings, FBandCache);
|
FSettings.LoadDevice(FDevMAC, G_Settings, FBandCache);
|
||||||
|
FCATLastGlobal := G_Settings;
|
||||||
|
CATApplySettings(G_Settings);
|
||||||
FVolume := G_Settings.Volume;
|
FVolume := G_Settings.Volume;
|
||||||
FActiveVfo := G_Settings.ActiveVfo;
|
FActiveVfo := G_Settings.ActiveVfo;
|
||||||
// PA settings
|
// PA settings
|
||||||
@@ -5790,9 +5852,19 @@ begin
|
|||||||
SF.OnVisibilityChange := ApplyVisibility;
|
SF.OnVisibilityChange := ApplyVisibility;
|
||||||
SF.OnFPSChange := ApplyFPS;
|
SF.OnFPSChange := ApplyFPS;
|
||||||
SF.OnPAChange := OnPASettingsChange;
|
SF.OnPAChange := OnPASettingsChange;
|
||||||
|
SF.OnCATChange := OnCATSettingsChange;
|
||||||
end;
|
end;
|
||||||
SF := TSettingsForm(FSettingsForm);
|
SF := TSettingsForm(FSettingsForm);
|
||||||
SF.LoadPASettings(FPAMaxPower, FPABandCal);
|
SF.LoadPASettings(FPAMaxPower, FPABandCal);
|
||||||
|
SF.LoadCATSettings(
|
||||||
|
FCATLastGlobal.CATSerialEnabled,
|
||||||
|
FCATLastGlobal.CATSerialPort,
|
||||||
|
FCATLastGlobal.CATSerialBaud,
|
||||||
|
FCATLastGlobal.CATSerialDataBits,
|
||||||
|
FCATLastGlobal.CATSerialStopBits,
|
||||||
|
FCATLastGlobal.CATSerialParity,
|
||||||
|
FCATLastGlobal.CATTcpEnabled,
|
||||||
|
FCATLastGlobal.CATTcpPort);
|
||||||
// Перечисляем PA устройства
|
// Перечисляем PA устройства
|
||||||
SF.RefreshAudioDevices(FAudioOut, FAudioIn);
|
SF.RefreshAudioDevices(FAudioOut, FAudioIn);
|
||||||
SF.LoadVisibility(FShowSpectrum, FShowWaterfall);
|
SF.LoadVisibility(FShowSpectrum, FShowWaterfall);
|
||||||
@@ -5841,5 +5913,240 @@ begin
|
|||||||
EnsureWDSPWisdom;
|
EnsureWDSPWisdom;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// CAT subsystem
|
||||||
|
// ===========================================================================
|
||||||
|
|
||||||
|
procedure TMainForm.InitCATEngine;
|
||||||
|
var
|
||||||
|
Ctx: TCATContext;
|
||||||
|
begin
|
||||||
|
FillChar(Ctx, SizeOf(Ctx), 0);
|
||||||
|
Ctx.GetVfoA := CATGetVfoA;
|
||||||
|
Ctx.GetVfoB := CATGetVfoB;
|
||||||
|
Ctx.GetMode := CATGetMode;
|
||||||
|
Ctx.GetActiveVfo := CATGetActiveVfo;
|
||||||
|
Ctx.GetAGCMode := CATGetAGCMode;
|
||||||
|
Ctx.GetVolume := CATGetVolume;
|
||||||
|
Ctx.GetDriveLevel := CATGetDriveLevel;
|
||||||
|
Ctx.GetFilterIdx := CATGetFilterIdx;
|
||||||
|
Ctx.GetFilterBW := CATGetFilterBW;
|
||||||
|
Ctx.GetNRMode := CATGetNRMode;
|
||||||
|
Ctx.GetNBMode := CATGetNBMode;
|
||||||
|
Ctx.GetSNBEnabled := CATGetSNB;
|
||||||
|
Ctx.GetANFEnabled := CATGetANF;
|
||||||
|
Ctx.GetTransmitting := CATGetTX;
|
||||||
|
Ctx.GetRunning := CATGetRunning;
|
||||||
|
Ctx.GetSMeter := CATGetSMeter;
|
||||||
|
Ctx.GetCurrentBand := CATGetBand;
|
||||||
|
Ctx.SetVfoA := CATSetVfoA;
|
||||||
|
Ctx.SetVfoB := CATSetVfoB;
|
||||||
|
Ctx.SetMode := WebOnMode;
|
||||||
|
Ctx.SetActiveVfo := WebOnActiveVfo;
|
||||||
|
Ctx.SetAGCMode := WebOnAGC;
|
||||||
|
Ctx.SetVolume := WebOnVolume;
|
||||||
|
Ctx.SetDriveLevel := WebOnDrive;
|
||||||
|
Ctx.SetFilterIdx := CATSetFilterIdx;
|
||||||
|
Ctx.SetNRMode := WebOnNR;
|
||||||
|
Ctx.SetNBMode := WebOnNB;
|
||||||
|
Ctx.SetSNBEnabled := WebOnSNB;
|
||||||
|
Ctx.SetANFEnabled := WebOnANF;
|
||||||
|
Ctx.SetTransmitting := WebOnMOX;
|
||||||
|
Ctx.DoBandUp := CATDoBandUp;
|
||||||
|
Ctx.DoBandDown := CATDoBandDown;
|
||||||
|
Ctx.DoTuneUp := CATDoTuneUp;
|
||||||
|
Ctx.DoTuneDown := CATDoTuneDown;
|
||||||
|
Ctx.DoBandByIndex := WebOnBand;
|
||||||
|
FCATEngine := TCATEngine.Create(Ctx);
|
||||||
|
FCATSerial := TCATSerialManager.Create(FCATEngine);
|
||||||
|
FCATTcp := TCATTcpServer.Create(FCATEngine);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TMainForm.CATApplySettings(const G: TGlobalSettings);
|
||||||
|
var
|
||||||
|
Cfgs: array[0..3] of TCATSerialConfig;
|
||||||
|
i: Integer;
|
||||||
|
begin
|
||||||
|
for i := 0 to 3 do
|
||||||
|
begin
|
||||||
|
Cfgs[i].Enabled := G.CATSerialEnabled[i];
|
||||||
|
Cfgs[i].PortName := G.CATSerialPort[i];
|
||||||
|
Cfgs[i].BaudRate := G.CATSerialBaud[i];
|
||||||
|
Cfgs[i].DataBits := G.CATSerialDataBits[i];
|
||||||
|
Cfgs[i].StopBits := G.CATSerialStopBits[i];
|
||||||
|
case G.CATSerialParity[i] of
|
||||||
|
1: Cfgs[i].Parity := cspOdd;
|
||||||
|
2: Cfgs[i].Parity := cspEven;
|
||||||
|
else Cfgs[i].Parity := cspNone;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
FCATSerial.ApplyConfig(Cfgs);
|
||||||
|
FCATTcp.Stop;
|
||||||
|
if G.CATTcpEnabled then
|
||||||
|
begin
|
||||||
|
FCATTcp.Port := G.CATTcpPort;
|
||||||
|
FCATTcp.Start;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TMainForm.OnCATSettingsChange(
|
||||||
|
const SerEnabled: array of Boolean;
|
||||||
|
const SerPort: array of string;
|
||||||
|
const SerBaud, SerDataBits, SerStopBits, SerParity: array of Integer;
|
||||||
|
TcpEnabled: Boolean; TcpPort: Integer);
|
||||||
|
var i: Integer;
|
||||||
|
begin
|
||||||
|
for i := 0 to 3 do
|
||||||
|
begin
|
||||||
|
if i <= High(SerEnabled) then FCATLastGlobal.CATSerialEnabled[i] := SerEnabled[i];
|
||||||
|
if i <= High(SerPort) then FCATLastGlobal.CATSerialPort[i] := SerPort[i];
|
||||||
|
if i <= High(SerBaud) then FCATLastGlobal.CATSerialBaud[i] := SerBaud[i];
|
||||||
|
if i <= High(SerDataBits) then FCATLastGlobal.CATSerialDataBits[i] := SerDataBits[i];
|
||||||
|
if i <= High(SerStopBits) then FCATLastGlobal.CATSerialStopBits[i] := SerStopBits[i];
|
||||||
|
if i <= High(SerParity) then FCATLastGlobal.CATSerialParity[i] := SerParity[i];
|
||||||
|
end;
|
||||||
|
FCATLastGlobal.CATTcpEnabled := TcpEnabled;
|
||||||
|
FCATLastGlobal.CATTcpPort := TcpPort;
|
||||||
|
CATApplySettings(FCATLastGlobal);
|
||||||
|
if FDevConnected then begin
|
||||||
|
FSettings.SaveGlobal(FDevMAC, MakeGlobalSettings);
|
||||||
|
FSettings.Save;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// --- Getters (called from CAT thread — read only, no sync needed) -----------
|
||||||
|
|
||||||
|
function TMainForm.CATGetVfoA: Double; begin Result := FVfoA; end;
|
||||||
|
function TMainForm.CATGetVfoB: Double; begin Result := FVfoB; end;
|
||||||
|
function TMainForm.CATGetMode: Integer; begin Result := FMode; end;
|
||||||
|
function TMainForm.CATGetActiveVfo: Integer; begin Result := FActiveVfo; end;
|
||||||
|
function TMainForm.CATGetAGCMode: Integer; begin Result := FAGCMode; end;
|
||||||
|
function TMainForm.CATGetVolume: Integer; begin Result := FVolume; end;
|
||||||
|
function TMainForm.CATGetDriveLevel: Integer;begin Result := TrkDrive.Position; end;
|
||||||
|
function TMainForm.CATGetFilterIdx: Integer; begin Result := FFilter; end;
|
||||||
|
function TMainForm.CATGetFilterBW: Integer; begin Result := FFilterBW; end;
|
||||||
|
function TMainForm.CATGetNRMode: Integer; begin Result := BtnNR.Tag; end;
|
||||||
|
function TMainForm.CATGetNBMode: Integer; begin Result := BtnNB.Tag; end;
|
||||||
|
function TMainForm.CATGetSNB: Boolean; begin Result := BtnSNB.Tag <> 0; end;
|
||||||
|
function TMainForm.CATGetANF: Boolean; begin Result := BtnANF.Tag <> 0; end;
|
||||||
|
function TMainForm.CATGetTX: Boolean; begin Result := FTransmitting; end;
|
||||||
|
function TMainForm.CATGetRunning: Boolean; begin Result := FRunning; end;
|
||||||
|
function TMainForm.CATGetSMeter: Double; begin Result := FLastSMeter; end;
|
||||||
|
function TMainForm.CATGetBand: Integer; begin Result := FCurrentBand; end;
|
||||||
|
|
||||||
|
// --- Setters ----------------------------------------------------------------
|
||||||
|
|
||||||
|
procedure TMainForm.CATSetVfoA(V: Double);
|
||||||
|
begin
|
||||||
|
FCATSyncFreq := V;
|
||||||
|
TThread.Synchronize(nil, SyncCATVfoA);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TMainForm.CATSetVfoB(V: Double);
|
||||||
|
begin
|
||||||
|
FCATSyncFreq := V;
|
||||||
|
TThread.Synchronize(nil, SyncCATVfoB);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TMainForm.CATSetFilterIdx(V: Integer);
|
||||||
|
begin
|
||||||
|
// WebOnFilter: negative value encodes 0-based index as -(idx+1)
|
||||||
|
WebOnFilter(-(V + 1));
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TMainForm.CATDoBandUp;
|
||||||
|
begin TThread.Synchronize(nil, SyncCATBandUp); end;
|
||||||
|
|
||||||
|
procedure TMainForm.CATDoBandDown;
|
||||||
|
begin TThread.Synchronize(nil, SyncCATBandDown); end;
|
||||||
|
|
||||||
|
procedure TMainForm.CATDoTuneUp;
|
||||||
|
begin TThread.Synchronize(nil, SyncCATTuneUp); end;
|
||||||
|
|
||||||
|
procedure TMainForm.CATDoTuneDown;
|
||||||
|
begin TThread.Synchronize(nil, SyncCATTuneDown); end;
|
||||||
|
|
||||||
|
// --- Sync methods (run in main thread) --------------------------------------
|
||||||
|
|
||||||
|
procedure TMainForm.SyncCATVfoA;
|
||||||
|
var BandIdx: Integer;
|
||||||
|
begin
|
||||||
|
FVfoA := FCATSyncFreq;
|
||||||
|
if FActiveVfo = 0 then
|
||||||
|
begin
|
||||||
|
ApplyVfoA(Round(FVfoA));
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
// VFO A is not active — just update display and band indicator
|
||||||
|
FreqDispA.Frequency := Round(FVfoA);
|
||||||
|
BandIdx := FreqToBandIdx(FVfoA);
|
||||||
|
if (BandIdx >= 0) and (BandIdx <> FCurrentBand) then
|
||||||
|
begin
|
||||||
|
StyleButton(BtnBand[FCurrentBand], False);
|
||||||
|
FCurrentBand := BandIdx;
|
||||||
|
StyleButton(BtnBand[FCurrentBand], True);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TMainForm.SyncCATVfoB;
|
||||||
|
var BandIdx: Integer;
|
||||||
|
begin
|
||||||
|
FVfoB := FCATSyncFreq;
|
||||||
|
FreqDispB.Frequency := Round(FVfoB);
|
||||||
|
if FActiveVfo = 1 then
|
||||||
|
begin
|
||||||
|
FCenterFreq := FVfoB;
|
||||||
|
if FWDSPReady then FDSPEngine.SetShift(0.0);
|
||||||
|
if FRunning then
|
||||||
|
FNetwork.SetRunAndFreq(True, FCenterFreq, FCenterFreq, FDriveLevel);
|
||||||
|
BandIdx := FreqToBandIdx(FVfoB);
|
||||||
|
if (BandIdx >= 0) and (BandIdx <> FCurrentBand) then
|
||||||
|
begin
|
||||||
|
StyleButton(BtnBand[FCurrentBand], False);
|
||||||
|
FCurrentBand := BandIdx;
|
||||||
|
StyleButton(BtnBand[FCurrentBand], True);
|
||||||
|
end;
|
||||||
|
FRulerLastFreq := -1.0; FRulerLastVfo := -1.0;
|
||||||
|
DrawSpectrum; PbSpectrum.Invalidate;
|
||||||
|
if PbRuler <> nil then PbRuler.Invalidate;
|
||||||
|
UpdateVfoDisplay;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TMainForm.SyncCATBandUp;
|
||||||
|
begin
|
||||||
|
if FCurrentBand < BAND_COUNT - 1 then
|
||||||
|
WebOnBand(FCurrentBand + 1);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TMainForm.SyncCATBandDown;
|
||||||
|
begin
|
||||||
|
if FCurrentBand > 0 then
|
||||||
|
WebOnBand(FCurrentBand - 1);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TMainForm.SyncCATTuneUp;
|
||||||
|
begin
|
||||||
|
if FActiveVfo = 0 then
|
||||||
|
ApplyVfoA(Round(FVfoA) + 10)
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
FCATSyncFreq := FVfoB + 10;
|
||||||
|
SyncCATVfoB;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TMainForm.SyncCATTuneDown;
|
||||||
|
begin
|
||||||
|
if FActiveVfo = 0 then
|
||||||
|
ApplyVfoA(Round(FVfoA) - 10)
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
FCATSyncFreq := FVfoB - 10;
|
||||||
|
SyncCATVfoB;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
@@ -75,6 +75,17 @@ type
|
|||||||
// --- PA (Power Amplifier) settings ---
|
// --- PA (Power Amplifier) settings ---
|
||||||
PAMaxPower: Double; // максимальная выходная мощность, Вт (5..200)
|
PAMaxPower: Double; // максимальная выходная мощность, Вт (5..200)
|
||||||
PABandCal: array[0..CFG_BAND_COUNT-1] of Double; // калибровка на диапазон 38.8..100.0
|
PABandCal: array[0..CFG_BAND_COUNT-1] of Double; // калибровка на диапазон 38.8..100.0
|
||||||
|
// --- CAT (Computer Aided Transceiver) settings ---
|
||||||
|
// До 4 последовательных портов (Linux: /dev/ttyS0, Windows: COM1 и т.д.)
|
||||||
|
CATSerialEnabled: array[0..3] of Boolean;
|
||||||
|
CATSerialPort: array[0..3] of string; // имя порта ОС
|
||||||
|
CATSerialBaud: array[0..3] of Integer; // скорость: 1200..115200
|
||||||
|
CATSerialDataBits: array[0..3] of Integer; // 7 или 8
|
||||||
|
CATSerialStopBits: array[0..3] of Integer; // 1 или 2
|
||||||
|
CATSerialParity: array[0..3] of Integer; // 0=None, 1=Odd, 2=Even
|
||||||
|
// TCP CAT сервер (один, многоклиентский)
|
||||||
|
CATTcpEnabled: Boolean;
|
||||||
|
CATTcpPort: Integer; // порт, по умолчанию 4992
|
||||||
end;
|
end;
|
||||||
|
|
||||||
TSettingsManager = class
|
TSettingsManager = class
|
||||||
@@ -191,6 +202,21 @@ begin
|
|||||||
G.ShowSpectrum := True;
|
G.ShowSpectrum := True;
|
||||||
G.ShowWaterfall := True;
|
G.ShowWaterfall := True;
|
||||||
G.DisplayFPS := 60;
|
G.DisplayFPS := 60;
|
||||||
|
// CAT defaults
|
||||||
|
for i := 0 to 3 do begin
|
||||||
|
G.CATSerialEnabled[i] := False;
|
||||||
|
{$IFDEF MSWINDOWS}
|
||||||
|
G.CATSerialPort[i] := 'COM' + IntToStr(i+1);
|
||||||
|
{$ELSE}
|
||||||
|
G.CATSerialPort[i] := '/dev/ttyS' + IntToStr(i);
|
||||||
|
{$ENDIF}
|
||||||
|
G.CATSerialBaud[i] := 9600;
|
||||||
|
G.CATSerialDataBits[i] := 8;
|
||||||
|
G.CATSerialStopBits[i] := 1;
|
||||||
|
G.CATSerialParity[i] := 0; // None
|
||||||
|
end;
|
||||||
|
G.CATTcpEnabled := False;
|
||||||
|
G.CATTcpPort := 4992;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
constructor TSettingsManager.Create(const FilePath: string);
|
constructor TSettingsManager.Create(const FilePath: string);
|
||||||
@@ -388,6 +414,21 @@ begin
|
|||||||
G.PAMaxPower := JD(GObj,'pa_max_power',100.0);
|
G.PAMaxPower := JD(GObj,'pa_max_power',100.0);
|
||||||
for i := 0 to CFG_BAND_COUNT-1 do
|
for i := 0 to CFG_BAND_COUNT-1 do
|
||||||
G.PABandCal[i] := JD(GObj,'pa_band_cal_'+IntToStr(i),100.0);
|
G.PABandCal[i] := JD(GObj,'pa_band_cal_'+IntToStr(i),100.0);
|
||||||
|
// CAT serial settings
|
||||||
|
for i := 0 to 3 do begin
|
||||||
|
G.CATSerialEnabled[i] := JB(GObj,'cat_serial_en_'+IntToStr(i), False);
|
||||||
|
{$IFDEF MSWINDOWS}
|
||||||
|
G.CATSerialPort[i] := JS(GObj,'cat_serial_port_'+IntToStr(i), 'COM'+IntToStr(i+1));
|
||||||
|
{$ELSE}
|
||||||
|
G.CATSerialPort[i] := JS(GObj,'cat_serial_port_'+IntToStr(i), '/dev/ttyS'+IntToStr(i));
|
||||||
|
{$ENDIF}
|
||||||
|
G.CATSerialBaud[i] := JI(GObj,'cat_serial_baud_'+IntToStr(i), 9600);
|
||||||
|
G.CATSerialDataBits[i] := JI(GObj,'cat_serial_dbits_'+IntToStr(i), 8);
|
||||||
|
G.CATSerialStopBits[i] := JI(GObj,'cat_serial_sbits_'+IntToStr(i), 1);
|
||||||
|
G.CATSerialParity[i] := JI(GObj,'cat_serial_parity_'+IntToStr(i), 0);
|
||||||
|
end;
|
||||||
|
G.CATTcpEnabled := JB(GObj,'cat_tcp_enabled', False);
|
||||||
|
G.CATTcpPort := JI(GObj,'cat_tcp_port', 4992);
|
||||||
|
|
||||||
for i := 0 to CFG_BAND_COUNT-1 do
|
for i := 0 to CFG_BAND_COUNT-1 do
|
||||||
begin
|
begin
|
||||||
@@ -449,6 +490,17 @@ begin
|
|||||||
JW(O,'pa_max_power',G.PAMaxPower);
|
JW(O,'pa_max_power',G.PAMaxPower);
|
||||||
for i := 0 to CFG_BAND_COUNT-1 do
|
for i := 0 to CFG_BAND_COUNT-1 do
|
||||||
JW(O,'pa_band_cal_'+IntToStr(i),G.PABandCal[i]);
|
JW(O,'pa_band_cal_'+IntToStr(i),G.PABandCal[i]);
|
||||||
|
// CAT serial settings
|
||||||
|
for i := 0 to 3 do begin
|
||||||
|
JW(O,'cat_serial_en_'+IntToStr(i), G.CATSerialEnabled[i]);
|
||||||
|
JWS(O,'cat_serial_port_'+IntToStr(i),G.CATSerialPort[i]);
|
||||||
|
JW(O,'cat_serial_baud_'+IntToStr(i), G.CATSerialBaud[i]);
|
||||||
|
JW(O,'cat_serial_dbits_'+IntToStr(i),G.CATSerialDataBits[i]);
|
||||||
|
JW(O,'cat_serial_sbits_'+IntToStr(i),G.CATSerialStopBits[i]);
|
||||||
|
JW(O,'cat_serial_parity_'+IntToStr(i),G.CATSerialParity[i]);
|
||||||
|
end;
|
||||||
|
JW(O,'cat_tcp_enabled',G.CATTcpEnabled);
|
||||||
|
JW(O,'cat_tcp_port',G.CATTcpPort);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TSettingsManager.SaveBand(const MAC: array of Byte; BandIdx: Integer;
|
procedure TSettingsManager.SaveBand(const MAC: array of Byte; BandIdx: Integer;
|
||||||
|
|||||||
+251
-1
@@ -43,6 +43,14 @@ type
|
|||||||
TOnAudioInDevChange = procedure(DevIndex: Integer; const DevName: string) of object;
|
TOnAudioInDevChange = procedure(DevIndex: Integer; const DevName: string) of object;
|
||||||
TOnVisibilityChange = procedure(ShowSpectrum, ShowWaterfall: Boolean) of object;
|
TOnVisibilityChange = procedure(ShowSpectrum, ShowWaterfall: Boolean) of object;
|
||||||
TOnFPSChange = procedure(FPS: Integer) of object;
|
TOnFPSChange = procedure(FPS: Integer) of object;
|
||||||
|
TOnCATChange = procedure(
|
||||||
|
const SerEnabled: array of Boolean;
|
||||||
|
const SerPort: array of string;
|
||||||
|
const SerBaud: array of Integer;
|
||||||
|
const SerDataBits: array of Integer;
|
||||||
|
const SerStopBits: array of Integer;
|
||||||
|
const SerParity: array of Integer;
|
||||||
|
TcpEnabled: Boolean; TcpPort: Integer) of object;
|
||||||
|
|
||||||
{ TSettingsForm }
|
{ TSettingsForm }
|
||||||
TSettingsForm = class(TForm)
|
TSettingsForm = class(TForm)
|
||||||
@@ -56,12 +64,14 @@ type
|
|||||||
FPageWaterfall: TScrollBox;
|
FPageWaterfall: TScrollBox;
|
||||||
FPagePA: TScrollBox;
|
FPagePA: TScrollBox;
|
||||||
FPageAdvanced: TScrollBox;
|
FPageAdvanced: TScrollBox;
|
||||||
|
FPageCAT: TScrollBox;
|
||||||
FNavAudio: TFlatButton;
|
FNavAudio: TFlatButton;
|
||||||
FNavDisplay: TFlatButton;
|
FNavDisplay: TFlatButton;
|
||||||
FNavSpectrum: TFlatButton;
|
FNavSpectrum: TFlatButton;
|
||||||
FNavWaterfall: TFlatButton;
|
FNavWaterfall: TFlatButton;
|
||||||
FNavPA: TFlatButton;
|
FNavPA: TFlatButton;
|
||||||
FNavAdvanced: TFlatButton;
|
FNavAdvanced: TFlatButton;
|
||||||
|
FNavCAT: TFlatButton;
|
||||||
|
|
||||||
// ---- Audio tab controls ----
|
// ---- Audio tab controls ----
|
||||||
FCmbRXDev: TComboBox;
|
FCmbRXDev: TComboBox;
|
||||||
@@ -96,10 +106,21 @@ type
|
|||||||
FEdMaxPower: TSpinEdit;
|
FEdMaxPower: TSpinEdit;
|
||||||
FEdBandCal: array[0..10] of TFloatSpinEdit;
|
FEdBandCal: array[0..10] of TFloatSpinEdit;
|
||||||
|
|
||||||
|
// ---- CAT tab controls ----
|
||||||
|
FCATSerialEn: array[0..3] of TCheckBox;
|
||||||
|
FCATSerialPort: array[0..3] of TEdit;
|
||||||
|
FCATSerialBaud: array[0..3] of TComboBox;
|
||||||
|
FCATSerialDataBits: array[0..3] of TComboBox;
|
||||||
|
FCATSerialStopBits: array[0..3] of TComboBox;
|
||||||
|
FCATSerialParity: array[0..3] of TComboBox;
|
||||||
|
FCATTcpEn: TCheckBox;
|
||||||
|
FCATTcpPort: TSpinEdit;
|
||||||
|
|
||||||
// ---- Close button ----
|
// ---- Close button ----
|
||||||
FBtnClose: TFlatButton;
|
FBtnClose: TFlatButton;
|
||||||
|
|
||||||
// ---- Callbacks ----
|
// ---- Callbacks ----
|
||||||
|
FOnCATChange: TOnCATChange;
|
||||||
FOnPAChange: TOnPASettingsChange;
|
FOnPAChange: TOnPASettingsChange;
|
||||||
FOnDisplayChange: TOnDisplayParamChange;
|
FOnDisplayChange: TOnDisplayParamChange;
|
||||||
FOnWaterfallChange: TOnWaterfallParamChange;
|
FOnWaterfallChange: TOnWaterfallParamChange;
|
||||||
@@ -116,6 +137,7 @@ type
|
|||||||
procedure BuildDisplayTab;
|
procedure BuildDisplayTab;
|
||||||
procedure BuildRX1Tab;
|
procedure BuildRX1Tab;
|
||||||
procedure BuildPATab;
|
procedure BuildPATab;
|
||||||
|
procedure BuildCATTab;
|
||||||
procedure ApplyTheme;
|
procedure ApplyTheme;
|
||||||
procedure NavClick(Sender: TObject);
|
procedure NavClick(Sender: TObject);
|
||||||
procedure SelectPage(APage: TWinControl; ANav: TFlatButton);
|
procedure SelectPage(APage: TWinControl; ANav: TFlatButton);
|
||||||
@@ -147,6 +169,9 @@ type
|
|||||||
procedure OnFPSCmbChange(Sender: TObject);
|
procedure OnFPSCmbChange(Sender: TObject);
|
||||||
procedure OnMaxPowerChange(Sender: TObject);
|
procedure OnMaxPowerChange(Sender: TObject);
|
||||||
procedure OnBandCalChange(Sender: TObject);
|
procedure OnBandCalChange(Sender: TObject);
|
||||||
|
procedure OnCATSerialChange(Sender: TObject);
|
||||||
|
procedure OnCATTcpChange(Sender: TObject);
|
||||||
|
procedure FireCATChange;
|
||||||
procedure BtnCloseClick(Sender: TObject);
|
procedure BtnCloseClick(Sender: TObject);
|
||||||
|
|
||||||
procedure FireDisplayChange;
|
procedure FireDisplayChange;
|
||||||
@@ -179,7 +204,13 @@ type
|
|||||||
procedure LoadVisibility(ShowSpectrum, ShowWaterfall: Boolean);
|
procedure LoadVisibility(ShowSpectrum, ShowWaterfall: Boolean);
|
||||||
procedure LoadFPS(FPS: Integer);
|
procedure LoadFPS(FPS: Integer);
|
||||||
procedure LoadPASettings(MaxPower: Double; const BandCal: array of Double);
|
procedure LoadPASettings(MaxPower: Double; const BandCal: array of Double);
|
||||||
|
procedure LoadCATSettings(
|
||||||
|
const SerEnabled: array of Boolean;
|
||||||
|
const SerPort: array of string;
|
||||||
|
const SerBaud, SerDataBits, SerStopBits, SerParity: array of Integer;
|
||||||
|
TcpEnabled: Boolean; TcpPort: Integer);
|
||||||
|
|
||||||
|
property OnCATChange: TOnCATChange read FOnCATChange write FOnCATChange;
|
||||||
property OnPAChange: TOnPASettingsChange read FOnPAChange write FOnPAChange;
|
property OnPAChange: TOnPASettingsChange read FOnPAChange write FOnPAChange;
|
||||||
property OnDisplayChange: TOnDisplayParamChange read FOnDisplayChange write FOnDisplayChange;
|
property OnDisplayChange: TOnDisplayParamChange read FOnDisplayChange write FOnDisplayChange;
|
||||||
property OnWaterfallChange: TOnWaterfallParamChange read FOnWaterfallChange write FOnWaterfallChange;
|
property OnWaterfallChange: TOnWaterfallParamChange read FOnWaterfallChange write FOnWaterfallChange;
|
||||||
@@ -209,6 +240,9 @@ const
|
|||||||
BTN_H = 24;
|
BTN_H = 24;
|
||||||
GRP_TITLE_H = 22; // высота заголовка в group-панелях (title label + divider)
|
GRP_TITLE_H = 22; // высота заголовка в group-панелях (title label + divider)
|
||||||
|
|
||||||
|
CAT_BAUD_VALS: array[0..7] of Integer = (1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200);
|
||||||
|
CAT_BAUD_NAMES: array[0..7] of string = ('1200','2400','4800','9600','19200','38400','57600','115200');
|
||||||
|
|
||||||
FFT_SIZES: array[0..6] of Integer = (4096, 8192, 16384, 32768, 65536, 131072, 262144);
|
FFT_SIZES: array[0..6] of Integer = (4096, 8192, 16384, 32768, 65536, 131072, 262144);
|
||||||
FFT_NAMES: array[0..6] of string = ('4096','8192','16384','32768','65536','131072','262144');
|
FFT_NAMES: array[0..6] of string = ('4096','8192','16384','32768','65536','131072','262144');
|
||||||
|
|
||||||
@@ -391,6 +425,7 @@ begin
|
|||||||
FNavWaterfall := MakeNavButton('Waterfall', 182);
|
FNavWaterfall := MakeNavButton('Waterfall', 182);
|
||||||
FNavPA := MakeNavButton('Power Amplifier', 218);
|
FNavPA := MakeNavButton('Power Amplifier', 218);
|
||||||
FNavAdvanced := MakeNavButton('Advanced', 254);
|
FNavAdvanced := MakeNavButton('Advanced', 254);
|
||||||
|
FNavCAT := MakeNavButton('CAT', 290);
|
||||||
|
|
||||||
FContentPanel := TPanel.Create(Self);
|
FContentPanel := TPanel.Create(Self);
|
||||||
FContentPanel.Parent := Self;
|
FContentPanel.Parent := Self;
|
||||||
@@ -404,11 +439,13 @@ begin
|
|||||||
FPageWaterfall := MakeScrollPage;
|
FPageWaterfall := MakeScrollPage;
|
||||||
FPagePA := MakeScrollPage;
|
FPagePA := MakeScrollPage;
|
||||||
FPageAdvanced := MakeScrollPage;
|
FPageAdvanced := MakeScrollPage;
|
||||||
|
FPageCAT := MakeScrollPage;
|
||||||
|
|
||||||
BuildAudioTab;
|
BuildAudioTab;
|
||||||
BuildDisplayTab;
|
BuildDisplayTab;
|
||||||
BuildRX1Tab;
|
BuildRX1Tab;
|
||||||
BuildPATab;
|
BuildPATab;
|
||||||
|
BuildCATTab;
|
||||||
|
|
||||||
FBtnClose := TFlatButton.Create(Self);
|
FBtnClose := TFlatButton.Create(Self);
|
||||||
FBtnClose.Parent := Self;
|
FBtnClose.Parent := Self;
|
||||||
@@ -435,7 +472,8 @@ begin
|
|||||||
else if Sender = FNavSpectrum then SelectPage(FPageSpectrum, FNavSpectrum)
|
else if Sender = FNavSpectrum then SelectPage(FPageSpectrum, FNavSpectrum)
|
||||||
else if Sender = FNavWaterfall then SelectPage(FPageWaterfall, FNavWaterfall)
|
else if Sender = FNavWaterfall then SelectPage(FPageWaterfall, FNavWaterfall)
|
||||||
else if Sender = FNavPA then SelectPage(FPagePA, FNavPA)
|
else if Sender = FNavPA then SelectPage(FPagePA, FNavPA)
|
||||||
else if Sender = FNavAdvanced then SelectPage(FPageAdvanced, FNavAdvanced);
|
else if Sender = FNavAdvanced then SelectPage(FPageAdvanced, FNavAdvanced)
|
||||||
|
else if Sender = FNavCAT then SelectPage(FPageCAT, FNavCAT);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TSettingsForm.SelectPage(APage: TWinControl; ANav: TFlatButton);
|
procedure TSettingsForm.SelectPage(APage: TWinControl; ANav: TFlatButton);
|
||||||
@@ -446,6 +484,7 @@ begin
|
|||||||
FPageWaterfall.Visible := APage = FPageWaterfall;
|
FPageWaterfall.Visible := APage = FPageWaterfall;
|
||||||
FPagePA.Visible := APage = FPagePA;
|
FPagePA.Visible := APage = FPagePA;
|
||||||
FPageAdvanced.Visible := APage = FPageAdvanced;
|
FPageAdvanced.Visible := APage = FPageAdvanced;
|
||||||
|
FPageCAT.Visible := APage = FPageCAT;
|
||||||
|
|
||||||
FNavAudio.Active := ANav = FNavAudio;
|
FNavAudio.Active := ANav = FNavAudio;
|
||||||
FNavDisplay.Active := ANav = FNavDisplay;
|
FNavDisplay.Active := ANav = FNavDisplay;
|
||||||
@@ -453,6 +492,7 @@ begin
|
|||||||
FNavWaterfall.Active := ANav = FNavWaterfall;
|
FNavWaterfall.Active := ANav = FNavWaterfall;
|
||||||
FNavPA.Active := ANav = FNavPA;
|
FNavPA.Active := ANav = FNavPA;
|
||||||
FNavAdvanced.Active := ANav = FNavAdvanced;
|
FNavAdvanced.Active := ANav = FNavAdvanced;
|
||||||
|
FNavCAT.Active := ANav = FNavCAT;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -859,6 +899,7 @@ begin
|
|||||||
if Assigned(FPageWaterfall) then FPageWaterfall.Color := CLR_BG;
|
if Assigned(FPageWaterfall) then FPageWaterfall.Color := CLR_BG;
|
||||||
if Assigned(FPagePA) then FPagePA.Color := CLR_BG;
|
if Assigned(FPagePA) then FPagePA.Color := CLR_BG;
|
||||||
if Assigned(FPageAdvanced) then FPageAdvanced.Color := CLR_BG;
|
if Assigned(FPageAdvanced) then FPageAdvanced.Color := CLR_BG;
|
||||||
|
if Assigned(FPageCAT) then FPageCAT.Color := CLR_BG;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -1294,4 +1335,213 @@ begin
|
|||||||
Close;
|
Close;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// BuildCATTab
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
procedure TSettingsForm.BuildCATTab;
|
||||||
|
const
|
||||||
|
MARGIN = 22;
|
||||||
|
PAD = 18;
|
||||||
|
LW = 120;
|
||||||
|
CX = PAD + LW + 8;
|
||||||
|
GRP_W = 320;
|
||||||
|
GRP_H = 240;
|
||||||
|
GRP_GAP = 16;
|
||||||
|
CMB_W = 140;
|
||||||
|
EDT_W = 160;
|
||||||
|
R1 = 42;
|
||||||
|
STEP = 32;
|
||||||
|
var
|
||||||
|
i, col, row, gx, gy, j: Integer;
|
||||||
|
Grp: TPanel;
|
||||||
|
Chk: TCheckBox;
|
||||||
|
Ed: TEdit;
|
||||||
|
Cmb: TComboBox;
|
||||||
|
Spin: TSpinEdit;
|
||||||
|
begin
|
||||||
|
with TLabel.Create(Self) do
|
||||||
|
begin
|
||||||
|
Parent := FPageCAT;
|
||||||
|
Caption := 'CAT';
|
||||||
|
SetBounds(MARGIN, 18, 400, 28);
|
||||||
|
Font.Name := UI_FONT; Font.Size := 16;
|
||||||
|
Font.Style := [fsBold]; Font.Color := CLR_TEXT;
|
||||||
|
end;
|
||||||
|
with TLabel.Create(Self) do
|
||||||
|
begin
|
||||||
|
Parent := FPageCAT;
|
||||||
|
Caption := 'Computer Aided Transceiver — serial port and TCP server settings.';
|
||||||
|
SetBounds(MARGIN, 48, 680, 20);
|
||||||
|
Font.Name := UI_FONT; Font.Size := 9; Font.Color := CLR_TEXTDIM;
|
||||||
|
end;
|
||||||
|
|
||||||
|
for i := 0 to 3 do
|
||||||
|
begin
|
||||||
|
row := i div 2;
|
||||||
|
col := i mod 2;
|
||||||
|
gx := MARGIN + col * (GRP_W + GRP_GAP);
|
||||||
|
gy := 86 + row * (GRP_H + GRP_GAP);
|
||||||
|
|
||||||
|
Grp := MakeGroupPanel(FPageCAT, 'Serial Port ' + IntToStr(i + 1), gx, gy, GRP_W, GRP_H);
|
||||||
|
|
||||||
|
Chk := TCheckBox.Create(Self);
|
||||||
|
Chk.Parent := Grp;
|
||||||
|
Chk.Caption := 'Enable';
|
||||||
|
Chk.SetBounds(PAD, R1, GRP_W - PAD * 2, 22);
|
||||||
|
Chk.Font.Color := CLR_TEXT;
|
||||||
|
Chk.Font.Name := UI_FONT; Chk.Font.Size := 9;
|
||||||
|
Chk.Tag := i;
|
||||||
|
Chk.OnChange := OnCATSerialChange;
|
||||||
|
FCATSerialEn[i] := Chk;
|
||||||
|
|
||||||
|
MakeLbl(Grp, 'Port:', PAD, R1 + STEP + 6, LW);
|
||||||
|
Ed := TEdit.Create(Self);
|
||||||
|
Ed.Parent := Grp;
|
||||||
|
Ed.SetBounds(CX, R1 + STEP, EDT_W, BTN_H);
|
||||||
|
Ed.Color := CLR_INPUT; Ed.Font.Color := CLR_INPUT_TEXT;
|
||||||
|
Ed.Font.Name := UI_FONT; Ed.Font.Size := 9;
|
||||||
|
Ed.Tag := i;
|
||||||
|
Ed.OnChange := OnCATSerialChange;
|
||||||
|
{$IFDEF MSWINDOWS}
|
||||||
|
Ed.Text := 'COM' + IntToStr(i + 1);
|
||||||
|
{$ELSE}
|
||||||
|
Ed.Text := '/dev/ttyS' + IntToStr(i);
|
||||||
|
{$ENDIF}
|
||||||
|
FCATSerialPort[i] := Ed;
|
||||||
|
|
||||||
|
MakeLbl(Grp, 'Baud rate:', PAD, R1 + 2 * STEP + 6, LW);
|
||||||
|
Cmb := MakeCombo(Grp, CX, R1 + 2 * STEP, CMB_W, OnCATSerialChange);
|
||||||
|
Cmb.Tag := i;
|
||||||
|
for j := 0 to High(CAT_BAUD_NAMES) do Cmb.Items.Add(CAT_BAUD_NAMES[j]);
|
||||||
|
Cmb.ItemIndex := 3; // 9600
|
||||||
|
FCATSerialBaud[i] := Cmb;
|
||||||
|
|
||||||
|
MakeLbl(Grp, 'Data bits:', PAD, R1 + 3 * STEP + 6, LW);
|
||||||
|
Cmb := MakeCombo(Grp, CX, R1 + 3 * STEP, CMB_W, OnCATSerialChange);
|
||||||
|
Cmb.Tag := i;
|
||||||
|
Cmb.Items.Add('7'); Cmb.Items.Add('8');
|
||||||
|
Cmb.ItemIndex := 1; // 8
|
||||||
|
FCATSerialDataBits[i] := Cmb;
|
||||||
|
|
||||||
|
MakeLbl(Grp, 'Stop bits:', PAD, R1 + 4 * STEP + 6, LW);
|
||||||
|
Cmb := MakeCombo(Grp, CX, R1 + 4 * STEP, CMB_W, OnCATSerialChange);
|
||||||
|
Cmb.Tag := i;
|
||||||
|
Cmb.Items.Add('1'); Cmb.Items.Add('2');
|
||||||
|
Cmb.ItemIndex := 0; // 1
|
||||||
|
FCATSerialStopBits[i] := Cmb;
|
||||||
|
|
||||||
|
MakeLbl(Grp, 'Parity:', PAD, R1 + 5 * STEP + 6, LW);
|
||||||
|
Cmb := MakeCombo(Grp, CX, R1 + 5 * STEP, CMB_W, OnCATSerialChange);
|
||||||
|
Cmb.Tag := i;
|
||||||
|
Cmb.Items.Add('None'); Cmb.Items.Add('Odd'); Cmb.Items.Add('Even');
|
||||||
|
Cmb.ItemIndex := 0; // None
|
||||||
|
FCATSerialParity[i] := Cmb;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// TCP server group below the two rows of serial port panels
|
||||||
|
gy := 86 + 2 * (GRP_H + GRP_GAP);
|
||||||
|
Grp := MakeGroupPanel(FPageCAT, 'TCP CAT Server', MARGIN, gy, GRP_W * 2 + GRP_GAP, 116);
|
||||||
|
|
||||||
|
FCATTcpEn := TCheckBox.Create(Self);
|
||||||
|
FCATTcpEn.Parent := Grp;
|
||||||
|
FCATTcpEn.Caption := 'Enable TCP server (default port 4992)';
|
||||||
|
FCATTcpEn.SetBounds(PAD, R1, 300, 22);
|
||||||
|
FCATTcpEn.Font.Color := CLR_TEXT;
|
||||||
|
FCATTcpEn.Font.Name := UI_FONT; FCATTcpEn.Font.Size := 9;
|
||||||
|
FCATTcpEn.OnChange := OnCATTcpChange;
|
||||||
|
|
||||||
|
MakeLbl(Grp, 'Port:', PAD, R1 + STEP + 6, LW);
|
||||||
|
Spin := TSpinEdit.Create(Self);
|
||||||
|
Spin.Parent := Grp;
|
||||||
|
Spin.SetBounds(CX, R1 + STEP, 100, BTN_H + 2);
|
||||||
|
Spin.Color := CLR_INPUT; Spin.Font.Color := CLR_INPUT_TEXT;
|
||||||
|
Spin.Font.Name := UI_FONT; Spin.Font.Size := 9;
|
||||||
|
Spin.MinValue := 1; Spin.MaxValue := 65535;
|
||||||
|
Spin.Value := 4992;
|
||||||
|
Spin.OnChange := OnCATTcpChange;
|
||||||
|
FCATTcpPort := Spin;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// CAT event handlers
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
procedure TSettingsForm.FireCATChange;
|
||||||
|
var
|
||||||
|
SerEn: array[0..3] of Boolean;
|
||||||
|
SerPort: array[0..3] of string;
|
||||||
|
SerBaud, SerData, SerStop, SerPar: array[0..3] of Integer;
|
||||||
|
i, Idx: Integer;
|
||||||
|
begin
|
||||||
|
if FLoading then Exit;
|
||||||
|
if not Assigned(FOnCATChange) then Exit;
|
||||||
|
for i := 0 to 3 do
|
||||||
|
begin
|
||||||
|
SerEn[i] := FCATSerialEn[i].Checked;
|
||||||
|
SerPort[i] := FCATSerialPort[i].Text;
|
||||||
|
Idx := FCATSerialBaud[i].ItemIndex;
|
||||||
|
if (Idx >= 0) and (Idx <= High(CAT_BAUD_VALS)) then
|
||||||
|
SerBaud[i] := CAT_BAUD_VALS[Idx]
|
||||||
|
else
|
||||||
|
SerBaud[i] := 9600;
|
||||||
|
if FCATSerialDataBits[i].ItemIndex = 0 then SerData[i] := 7 else SerData[i] := 8;
|
||||||
|
if FCATSerialStopBits[i].ItemIndex = 1 then SerStop[i] := 2 else SerStop[i] := 1;
|
||||||
|
SerPar[i] := FCATSerialParity[i].ItemIndex;
|
||||||
|
end;
|
||||||
|
FOnCATChange(SerEn, SerPort, SerBaud, SerData, SerStop, SerPar,
|
||||||
|
FCATTcpEn.Checked, FCATTcpPort.Value);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSettingsForm.OnCATSerialChange(Sender: TObject);
|
||||||
|
begin FireCATChange; end;
|
||||||
|
|
||||||
|
procedure TSettingsForm.OnCATTcpChange(Sender: TObject);
|
||||||
|
begin FireCATChange; end;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// LoadCATSettings
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
procedure TSettingsForm.LoadCATSettings(
|
||||||
|
const SerEnabled: array of Boolean;
|
||||||
|
const SerPort: array of string;
|
||||||
|
const SerBaud, SerDataBits, SerStopBits, SerParity: array of Integer;
|
||||||
|
TcpEnabled: Boolean; TcpPort: Integer);
|
||||||
|
var
|
||||||
|
i, j, Idx: Integer;
|
||||||
|
begin
|
||||||
|
FLoading := True;
|
||||||
|
try
|
||||||
|
for i := 0 to 3 do
|
||||||
|
begin
|
||||||
|
if i <= High(SerEnabled) then FCATSerialEn[i].Checked := SerEnabled[i];
|
||||||
|
if i <= High(SerPort) then FCATSerialPort[i].Text := SerPort[i];
|
||||||
|
if i <= High(SerBaud) then
|
||||||
|
begin
|
||||||
|
Idx := 3;
|
||||||
|
for j := 0 to High(CAT_BAUD_VALS) do
|
||||||
|
if CAT_BAUD_VALS[j] = SerBaud[i] then begin Idx := j; Break; end;
|
||||||
|
FCATSerialBaud[i].ItemIndex := Idx;
|
||||||
|
end;
|
||||||
|
if i <= High(SerDataBits) then
|
||||||
|
begin
|
||||||
|
if SerDataBits[i] = 7 then FCATSerialDataBits[i].ItemIndex := 0
|
||||||
|
else FCATSerialDataBits[i].ItemIndex := 1;
|
||||||
|
end;
|
||||||
|
if i <= High(SerStopBits) then
|
||||||
|
begin
|
||||||
|
if SerStopBits[i] = 2 then FCATSerialStopBits[i].ItemIndex := 1
|
||||||
|
else FCATSerialStopBits[i].ItemIndex := 0;
|
||||||
|
end;
|
||||||
|
if i <= High(SerParity) then
|
||||||
|
FCATSerialParity[i].ItemIndex := EnsureRange(SerParity[i], 0, 2);
|
||||||
|
end;
|
||||||
|
FCATTcpEn.Checked := TcpEnabled;
|
||||||
|
FCATTcpPort.Value := EnsureRange(TcpPort, 1, 65535);
|
||||||
|
finally
|
||||||
|
FLoading := False;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
@@ -139,6 +139,18 @@
|
|||||||
<Filename Value="Settings.pas"/>
|
<Filename Value="Settings.pas"/>
|
||||||
<IsPartOfProject Value="True"/>
|
<IsPartOfProject Value="True"/>
|
||||||
</Unit>
|
</Unit>
|
||||||
|
<Unit>
|
||||||
|
<Filename Value="CATEngine.pas"/>
|
||||||
|
<IsPartOfProject Value="True"/>
|
||||||
|
</Unit>
|
||||||
|
<Unit>
|
||||||
|
<Filename Value="CATSerial.pas"/>
|
||||||
|
<IsPartOfProject Value="True"/>
|
||||||
|
</Unit>
|
||||||
|
<Unit>
|
||||||
|
<Filename Value="CATTcp.pas"/>
|
||||||
|
<IsPartOfProject Value="True"/>
|
||||||
|
</Unit>
|
||||||
</Units>
|
</Units>
|
||||||
</ProjectOptions>
|
</ProjectOptions>
|
||||||
<CompilerOptions>
|
<CompilerOptions>
|
||||||
|
|||||||
Reference in New Issue
Block a user