mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 20:37:33 +00:00
fix(cat): транспорты — короткая запись в TCP и молчаливый отказ serial-порта
CATTcp.SendStr делал один send на ответ. TCP не обязан отдавать весь буфер за раз, а усечение здесь не ошибка — длинный ответ (IF, ZZEB, список режимов) мог уехать обрезанным, и молча. Дописываем остаток в цикле. CATSerial помечал порт активным ДО SerOpen, а открытие шло внутри потока: при отказе порт навсегда оставался «работающим» в ActiveCount и UI, а причина нигде не оседала. Открытие переехало в TCATSerialPort.Start и делается синхронно, так что отказ виден сразу — FActive остаётся False, причина в новом свойстве LastError. Поток теперь только читает, закрывает владелец в Stop. Заодно Andromeda-порт назначался по галке в настройках, без оглядки на то, поднялся ли порт: HasAndromeda рапортовал о панели, которой нет. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+70
-51
@@ -63,7 +63,6 @@ type
|
|||||||
FHandle: TSerialHandle;
|
FHandle: TSerialHandle;
|
||||||
FBuf: string;
|
FBuf: string;
|
||||||
procedure ProcessBuffer;
|
procedure ProcessBuffer;
|
||||||
function TryOpenPort: Boolean;
|
|
||||||
protected
|
protected
|
||||||
procedure Execute; override;
|
procedure Execute; override;
|
||||||
public
|
public
|
||||||
@@ -79,6 +78,9 @@ type
|
|||||||
FHandle: TSerialHandle;
|
FHandle: TSerialHandle;
|
||||||
FLock: TCriticalSection;
|
FLock: TCriticalSection;
|
||||||
FActive: Boolean;
|
FActive: Boolean;
|
||||||
|
FLastError: string;
|
||||||
|
function OpenPort: Boolean; // синхронно, из Start — чтобы отказ был виден сразу
|
||||||
|
procedure ClosePort;
|
||||||
public
|
public
|
||||||
constructor Create(AEngine: TCATEngine; const ACfg: TCATSerialConfig);
|
constructor Create(AEngine: TCATEngine; const ACfg: TCATSerialConfig);
|
||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
@@ -89,6 +91,9 @@ type
|
|||||||
property Config: TCATSerialConfig read FConfig;
|
property Config: TCATSerialConfig read FConfig;
|
||||||
property Engine: TCATEngine read FEngine;
|
property Engine: TCATEngine read FEngine;
|
||||||
property Active: Boolean read FActive;
|
property Active: Boolean read FActive;
|
||||||
|
// Причина, по которой порт не поднялся (пусто, если поднялся). Раньше отказ
|
||||||
|
// SerOpen нигде не оседал, а порт продолжал числиться активным.
|
||||||
|
property LastError: string read FLastError;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TCATSerialManager — manages up to 4 serial ports }
|
{ TCATSerialManager — manages up to 4 serial ports }
|
||||||
@@ -120,40 +125,6 @@ begin
|
|||||||
inherited Create(True);
|
inherited Create(True);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TCATSerialThread.TryOpenPort: Boolean;
|
|
||||||
{$IFNDEF CAT_SERIAL_STUB}
|
|
||||||
var
|
|
||||||
cfg: TCATSerialConfig;
|
|
||||||
h: TSerialHandle;
|
|
||||||
par: TParityType;
|
|
||||||
{$ENDIF}
|
|
||||||
begin
|
|
||||||
Result := False;
|
|
||||||
{$IFDEF CAT_SERIAL_STUB}
|
|
||||||
Exit;
|
|
||||||
{$ELSE}
|
|
||||||
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 := Serial.NoneParity;
|
|
||||||
cspOdd: par := Serial.OddParity;
|
|
||||||
cspEven: par := Serial.EvenParity;
|
|
||||||
else par := Serial.NoneParity;
|
|
||||||
end;
|
|
||||||
|
|
||||||
SerSetParams(h, cfg.BaudRate, cfg.DataBits, par, cfg.StopBits, []);
|
|
||||||
FHandle := h;
|
|
||||||
FOwner.Handle := h;
|
|
||||||
Result := True;
|
|
||||||
{$ENDIF}
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TCATSerialThread.ProcessBuffer;
|
procedure TCATSerialThread.ProcessBuffer;
|
||||||
var
|
var
|
||||||
tpos: Integer;
|
tpos: Integer;
|
||||||
@@ -173,6 +144,8 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TCATSerialThread.Execute;
|
procedure TCATSerialThread.Execute;
|
||||||
|
// Порт уже открыт владельцем (TCATSerialPort.Start) — поток только читает.
|
||||||
|
// Закрывает тоже владелец, в Stop, после WaitFor.
|
||||||
var
|
var
|
||||||
ch: Byte;
|
ch: Byte;
|
||||||
n: LongInt;
|
n: LongInt;
|
||||||
@@ -180,22 +153,13 @@ begin
|
|||||||
{$IFDEF CAT_SERIAL_STUB}
|
{$IFDEF CAT_SERIAL_STUB}
|
||||||
Exit;
|
Exit;
|
||||||
{$ELSE}
|
{$ELSE}
|
||||||
if not TryOpenPort then Exit;
|
FHandle := FOwner.Handle;
|
||||||
try
|
while not Terminated do begin
|
||||||
while not Terminated do begin
|
n := SerReadTimeout(FHandle, ch, 50);
|
||||||
n := SerReadTimeout(FHandle, ch, 50);
|
if n = 1 then begin
|
||||||
if n = 1 then begin
|
FBuf := FBuf + Chr(ch);
|
||||||
FBuf := FBuf + Chr(ch);
|
if ch = Ord(';') then ProcessBuffer;
|
||||||
if ch = Ord(';') then ProcessBuffer;
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
finally
|
|
||||||
SerClose(FHandle);
|
|
||||||
{$IFDEF MSWINDOWS}
|
|
||||||
FOwner.Handle := INVALID_HANDLE_VALUE;
|
|
||||||
{$ELSE}
|
|
||||||
FOwner.Handle := -1;
|
|
||||||
{$ENDIF}
|
|
||||||
end;
|
end;
|
||||||
{$ENDIF}
|
{$ENDIF}
|
||||||
end;
|
end;
|
||||||
@@ -223,9 +187,61 @@ begin
|
|||||||
inherited;
|
inherited;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TCATSerialPort.OpenPort: Boolean;
|
||||||
|
{$IFNDEF CAT_SERIAL_STUB}
|
||||||
|
var
|
||||||
|
h: TSerialHandle;
|
||||||
|
par: TParityType;
|
||||||
|
{$ENDIF}
|
||||||
|
begin
|
||||||
|
Result := False;
|
||||||
|
{$IFDEF CAT_SERIAL_STUB}
|
||||||
|
FLastError := 'serial support not built on this platform';
|
||||||
|
{$ELSE}
|
||||||
|
h := SerOpen(FConfig.PortName);
|
||||||
|
{$IFDEF MSWINDOWS}
|
||||||
|
if h = TSerialHandle(INVALID_HANDLE_VALUE) then
|
||||||
|
{$ELSE}
|
||||||
|
if h < 0 then
|
||||||
|
{$ENDIF}
|
||||||
|
begin
|
||||||
|
FLastError := 'не удалось открыть ' + FConfig.PortName;
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
case FConfig.Parity of
|
||||||
|
cspNone: par := Serial.NoneParity;
|
||||||
|
cspOdd: par := Serial.OddParity;
|
||||||
|
cspEven: par := Serial.EvenParity;
|
||||||
|
else par := Serial.NoneParity;
|
||||||
|
end;
|
||||||
|
|
||||||
|
SerSetParams(h, FConfig.BaudRate, FConfig.DataBits, par, FConfig.StopBits, []);
|
||||||
|
FHandle := h;
|
||||||
|
FLastError := '';
|
||||||
|
Result := True;
|
||||||
|
{$ENDIF}
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TCATSerialPort.ClosePort;
|
||||||
|
begin
|
||||||
|
{$IFNDEF CAT_SERIAL_STUB}
|
||||||
|
{$IFDEF MSWINDOWS}
|
||||||
|
if FHandle <> INVALID_HANDLE_VALUE then SerClose(FHandle);
|
||||||
|
FHandle := INVALID_HANDLE_VALUE;
|
||||||
|
{$ELSE}
|
||||||
|
if FHandle >= 0 then SerClose(FHandle);
|
||||||
|
FHandle := -1;
|
||||||
|
{$ENDIF}
|
||||||
|
{$ENDIF}
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TCATSerialPort.Start;
|
procedure TCATSerialPort.Start;
|
||||||
|
// Порт открываем ЗДЕСЬ, а не в потоке: иначе отказ SerOpen оставался внутри
|
||||||
|
// потока, а порт всё это время числился активным — и в ActiveCount, и в UI.
|
||||||
begin
|
begin
|
||||||
if FActive or not FConfig.Enabled then Exit;
|
if FActive or not FConfig.Enabled then Exit;
|
||||||
|
if not OpenPort then Exit; // FActive остаётся False, причина — в LastError
|
||||||
FActive := True;
|
FActive := True;
|
||||||
FThread := TCATSerialThread.Create(Self);
|
FThread := TCATSerialThread.Create(Self);
|
||||||
FThread.Start;
|
FThread.Start;
|
||||||
@@ -240,6 +256,7 @@ begin
|
|||||||
FThread.WaitFor;
|
FThread.WaitFor;
|
||||||
FreeAndNil(FThread);
|
FreeAndNil(FThread);
|
||||||
end;
|
end;
|
||||||
|
ClosePort;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TCATSerialPort.SendStr(const S: string);
|
procedure TCATSerialPort.SendStr(const S: string);
|
||||||
@@ -294,7 +311,9 @@ begin
|
|||||||
for i := 0 to cnt-1 do begin
|
for i := 0 to cnt-1 do begin
|
||||||
FPorts[i] := TCATSerialPort.Create(FEngine, Configs[i]);
|
FPorts[i] := TCATSerialPort.Create(FEngine, Configs[i]);
|
||||||
if Configs[i].Enabled then FPorts[i].Start;
|
if Configs[i].Enabled then FPorts[i].Start;
|
||||||
if Configs[i].Enabled and Configs[i].Andromeda and (FAndromedaPort = nil) then
|
// Панель назначаем только на РЕАЛЬНО поднявшийся порт: раньше хватало галки
|
||||||
|
// в настройках, и HasAndromeda рапортовал о панели, которой нет.
|
||||||
|
if FPorts[i].Active and Configs[i].Andromeda and (FAndromedaPort = nil) then
|
||||||
FAndromedaPort := FPorts[i];
|
FAndromedaPort := FPorts[i];
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|||||||
+21
-11
@@ -161,22 +161,32 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TCATTcpClientThread.SendStr(const S: string);
|
procedure TCATTcpClientThread.SendStr(const S: string);
|
||||||
|
// TCP не обязан отдавать весь буфер за один send: длинный ответ (IF, ZZEB,
|
||||||
|
// список режимов) мог уехать обрезанным — и молча, потому что усечение здесь
|
||||||
|
// не ошибка. Дописываем остаток, пока он есть.
|
||||||
var
|
var
|
||||||
buf: AnsiString;
|
buf: AnsiString;
|
||||||
n: Integer;
|
sent, n: Integer;
|
||||||
begin
|
begin
|
||||||
if FSocket = SOCK_INVALID then Exit;
|
if FSocket = SOCK_INVALID then Exit;
|
||||||
buf := AnsiString(S);
|
buf := AnsiString(S);
|
||||||
if Length(buf) = 0 then Exit;
|
if Length(buf) = 0 then Exit;
|
||||||
{$IFDEF MSWINDOWS}
|
sent := 0;
|
||||||
n := send(FSocket, buf[1], Length(buf), 0);
|
while sent < Length(buf) do
|
||||||
if n = SOCKET_ERROR then begin
|
begin
|
||||||
{$ELSE}
|
{$IFDEF MSWINDOWS}
|
||||||
n := fpSend(FSocket, @buf[1], Length(buf), 0);
|
n := send(FSocket, buf[sent + 1], Length(buf) - sent, 0);
|
||||||
if n <= 0 then begin
|
if n = SOCKET_ERROR then n := -1;
|
||||||
{$ENDIF}
|
{$ELSE}
|
||||||
FMarkDelete := True;
|
n := fpSend(FSocket, @buf[sent + 1], Length(buf) - sent, 0);
|
||||||
Terminate;
|
{$ENDIF}
|
||||||
|
if n <= 0 then
|
||||||
|
begin
|
||||||
|
FMarkDelete := True;
|
||||||
|
Terminate;
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
Inc(sent, n);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user