mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-27 02:17:33 +00:00
feat(tci): TCI 2.0 — EWSDR как сервер (команды и уведомления)
Протокол Expert Electronics поверх WebSocket, порт 40001. EWSDR слушает, клиенты — логгеры, скиммеры, цифровые программы. - TCIProtocol.pas — чистый слой протокола: разбор/сборка `имя:арг;`, экранирование `^ ~ *`, словарь видов связи, пересчёт громкости в дБ. - TCIServer.pas — WS-сервер поверх WebUtils/WsClient: accept-поток, поток на клиента, HTTP-Upgrade, фреймы, рассылка, тик 20 мс. - TCIAdapter.pas — мост к TRadioController по схеме CAT: геттеры читают поля напрямую, сеттеры через Invoke, уведомления через AddStateListener. Маппинг: приёмник TCI = панадаптер, канал A/B = VFO A/B (пан 0) либо первый/второй слайс (паны 1..). Попутно: TRadioController.RemoveStateListener (адаптер умирает раньше контроллера) и TDXSpotStore.RemoveCall (spot_delete). Настройки — секция "tci" в settings.json (умолчание: выключено, 127.0.0.1, так как авторизации в протоколе нет) и вкладка Advanced → TCI Server. Бинарные потоки (IQ/аудио) — этап 2. Статус, таблица команд и список осознанных эхо-заглушек — в doc/TCI.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -92,6 +92,8 @@ type
|
||||
|
||||
TOnWebSettingsChange = procedure(Enabled: Boolean; Port: Integer;
|
||||
const BindAddr, User, Pass: string; SpecPixels: Integer) of object;
|
||||
TOnTCISettingsChange = procedure(Enabled: Boolean; Port: Integer;
|
||||
const BindAddr: string) of object;
|
||||
TOnCATChange = procedure(
|
||||
const SerEnabled: array of Boolean;
|
||||
const SerPort: array of string;
|
||||
@@ -470,6 +472,9 @@ type
|
||||
FEdWebUser: TFlatEdit;
|
||||
FEdWebPass: TFlatEdit;
|
||||
FCmbWebRes: TFlatComboBox;
|
||||
FChkTCIEnabled: TFlatCheckBox;
|
||||
FEdTCIPort: TFlatSpinEdit;
|
||||
FEdTCIBind: TFlatEdit;
|
||||
|
||||
// ---- Close button ----
|
||||
FBtnClose: TFlatButton;
|
||||
@@ -483,6 +488,7 @@ type
|
||||
FOnVHFCalChange: TOnVHFCalChange;
|
||||
FOnADCChange: TOnADCChange;
|
||||
FOnWebSettingsChange: TOnWebSettingsChange;
|
||||
FOnTCISettingsChange: TOnTCISettingsChange;
|
||||
FOnDXClusterChange: TOnDXClusterChange;
|
||||
FOnDisplayChange: TOnDisplayParamChange;
|
||||
FOnWaterfallChange: TOnWaterfallParamChange;
|
||||
@@ -535,6 +541,7 @@ type
|
||||
procedure BuildAdvancedTab;
|
||||
procedure OnADCChkChange(Sender: TObject);
|
||||
procedure OnWebAnyChange(Sender: TObject);
|
||||
procedure OnTCIAnyChange(Sender: TObject);
|
||||
procedure CollectAlexFromUI;
|
||||
procedure FireAlexChange;
|
||||
procedure OnAlexAnyChange(Sender: TObject);
|
||||
@@ -720,6 +727,8 @@ type
|
||||
procedure LoadDXClusterSettings(const D: TDXClusterSettings);
|
||||
procedure LoadWebSettings(Enabled: Boolean; Port: Integer;
|
||||
const BindAddr, User, Pass: string; SpecPixels: Integer);
|
||||
procedure LoadTCISettings(Enabled: Boolean; Port: Integer;
|
||||
const BindAddr: string);
|
||||
|
||||
property OnCATChange: TOnCATChange read FOnCATChange write FOnCATChange;
|
||||
property OnSliceCATChange: TOnSliceCATChange read FOnSliceCATChange write FOnSliceCATChange;
|
||||
@@ -756,6 +765,7 @@ type
|
||||
property OnWfAGCNFChange: TOnWfAGCNFChange read FOnWfAGCNFChange write FOnWfAGCNFChange;
|
||||
property OnADCChange: TOnADCChange read FOnADCChange write FOnADCChange;
|
||||
property OnWebSettingsChange: TOnWebSettingsChange read FOnWebSettingsChange write FOnWebSettingsChange;
|
||||
property OnTCISettingsChange: TOnTCISettingsChange read FOnTCISettingsChange write FOnTCISettingsChange;
|
||||
property OnDXClusterChange: TOnDXClusterChange read FOnDXClusterChange write FOnDXClusterChange;
|
||||
end;
|
||||
|
||||
@@ -3441,6 +3451,52 @@ begin
|
||||
Lbl := MakeLbl(Grp, '1024 px ~160 KB/s, 4096 px ~650 KB/s per client',
|
||||
GRP_PAD + LBL_W + 12, Y + ROW_H - 8, 320);
|
||||
Lbl.Font.Size := 8;
|
||||
|
||||
// ── TCI Server ────────────────────────────────────────────────────────────
|
||||
// Протокол Expert Electronics поверх WebSocket: логгеры, скиммеры, цифра.
|
||||
Grp := MakeGroupPanel(FPageAdvanced, 'TCI Server', MARGIN, 564, SETTINGS_CARD_W, 190);
|
||||
|
||||
Y := R1;
|
||||
Chk := TFlatCheckBox.Create(Self);
|
||||
Chk.Parent := Grp;
|
||||
Chk.Caption := 'Enabled';
|
||||
Chk.SetBounds(DpiScale(GRP_PAD), DpiScale(Y), DpiScale(260), DpiScale(22));
|
||||
Chk.Font.Size := 9;
|
||||
Chk.Font.Color := CLR_TEXT;
|
||||
Chk.Checked := False;
|
||||
Chk.OnChange := OnTCIAnyChange;
|
||||
FChkTCIEnabled := Chk;
|
||||
|
||||
Y := Y + ROW_H;
|
||||
MakeLbl(Grp, 'Port:', GRP_PAD, Y + 4, LBL_W);
|
||||
Spin := TFlatSpinEdit.Create(Self);
|
||||
Spin.Parent := Grp;
|
||||
Spin.SetBounds(DpiScale(GRP_PAD + LBL_W + 12), DpiScale(Y), DpiScale(90), DpiScale(BTN_H));
|
||||
Spin.Color := CLR_INPUT;
|
||||
Spin.Font.Color := CLR_INPUT_TEXT;
|
||||
Spin.Font.Size := 9;
|
||||
Spin.MinValue := 1;
|
||||
Spin.MaxValue := 65535;
|
||||
Spin.Value := 40001;
|
||||
Spin.OnChange := OnTCIAnyChange;
|
||||
FEdTCIPort := Spin;
|
||||
|
||||
Y := Y + ROW_H;
|
||||
MakeLbl(Grp, 'Interface:', GRP_PAD, Y + 4, LBL_W);
|
||||
Ed := TFlatEdit.Create(Self);
|
||||
Ed.Parent := Grp;
|
||||
Ed.SetBounds(DpiScale(GRP_PAD + LBL_W + 12), DpiScale(Y), DpiScale(ED_W), DpiScale(BTN_H));
|
||||
Ed.Color := CLR_INPUT;
|
||||
Ed.Font.Color := CLR_INPUT_TEXT;
|
||||
Ed.Font.Size := 9;
|
||||
Ed.Text := '127.0.0.1';
|
||||
Ed.OnChange := OnTCIAnyChange;
|
||||
FEdTCIBind := Ed;
|
||||
|
||||
// Авторизации в протоколе нет: открытый наружу порт = полный доступ к трансиверу.
|
||||
Lbl := MakeLbl(Grp, 'No authentication in TCI — keep 127.0.0.1 unless the network is trusted',
|
||||
GRP_PAD, Y + ROW_H, 460);
|
||||
Lbl.Font.Size := 8;
|
||||
end;
|
||||
|
||||
procedure TSettingsForm.BuildDXClusterTab;
|
||||
@@ -3689,6 +3745,26 @@ begin
|
||||
Res);
|
||||
end;
|
||||
|
||||
procedure TSettingsForm.OnTCIAnyChange(Sender: TObject);
|
||||
begin
|
||||
if FLoading then Exit;
|
||||
if not Assigned(FOnTCISettingsChange) then Exit;
|
||||
FOnTCISettingsChange(FChkTCIEnabled.Checked, FEdTCIPort.Value, FEdTCIBind.Text);
|
||||
end;
|
||||
|
||||
procedure TSettingsForm.LoadTCISettings(Enabled: Boolean; Port: Integer;
|
||||
const BindAddr: string);
|
||||
begin
|
||||
FLoading := True;
|
||||
try
|
||||
FChkTCIEnabled.Checked := Enabled;
|
||||
FEdTCIPort.Value := EnsureRange(Port, 1, 65535);
|
||||
FEdTCIBind.Text := BindAddr;
|
||||
finally
|
||||
FLoading := False;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TSettingsForm.LoadADCSettings(Dither, Random: Boolean);
|
||||
begin
|
||||
FLoading := True;
|
||||
|
||||
Reference in New Issue
Block a user