Search (Ctrl+F): - in-terminal search bar over full scrollback + screen; all hits highlighted, current hit emphasized; Enter searches upward, Shift+Enter downward, F3/Esc shortcuts - hits anchored via ScreenBuffer.DroppedLines so positions stay correct as the ring buffer drops old lines Keyword highlighting & tab alerts: - new Settings -> Highlight page: user-defined keyword list with per-rule enable and a global toggle (AppSettings.KeywordRules) - keywords highlighted red in every terminal (visible rows only, case-insensitive); background tabs flash a red dot when a keyword appears, cleared when the tab is opened TTL engine (TeraTerm macro compatibility): - new TtlExpression parser: parens, and/or/xor/not, comparisons, * / % + -, hex literals (0x/$), string/int values; legacy fallback keeps old scripts working - control flow: goto, call/return (inline, usable inside loops), for/next, do/loop [while|until], until/enduntil, break, continue, end, exit, include, mpause; one-line "if <expr> <statement>" - waits: waitln, waitregex (matchstr/groupmatchstr1-9), recvln, multi-string wait (TeraTerm semantics), mtimeout - strings: strlen strcompare strconcat strcopy strinsert strremove strmatch strscan strreplace strtrim strsplit strjoin tolower toupper str2int int2str code2str str2code sprintf expandenv - files: fileopen filereadln filewrite(ln) fileclose filecreate filedelete filesearch basename dirname makepath foldercreate folderdelete foldersearch getdir setdir - misc: beep getdate gettime getenv setenv random exec getver getttdir uptime ifdefined clipb2var var2clipb inputbox yesnobox crc32 checksum8/16/32 dispstr - serial: sendbreak setbaud setdtr setrts sendfile (SerialChannel gains SendBreak/SetBaudRate/SetDtr/SetRts) - script Output (incl. dispstr) now echoed gray into the terminal - quote-aware comment stripping; case-insensitive variables Docs: - docs/ttl-script-reference.md rewritten: ETTerms-only commands first, then the TeraTerm-shared set, with examples Misc: - version 0.5.0; About changelog; CLAUDE.md v0.5.0 notes - restore ETTerms.PduCore ProjectReference in ETTerms/PduMcp csproj (was dropped in the working tree; required to compile) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
109 lines
3.2 KiB
C#
109 lines
3.2 KiB
C#
using System.IO.Ports;
|
||
using ETTerms.Connections;
|
||
using ETTerms.Infrastructure;
|
||
|
||
namespace ETTerms.Sessions;
|
||
|
||
/// <summary>
|
||
/// Serial 連線通道:包 <see cref="SerialPort"/>。開啟前向 SessionManager 占用 COM port(互斥)。
|
||
/// </summary>
|
||
public sealed class SerialChannel : ISessionChannel
|
||
{
|
||
private readonly SerialPort _port;
|
||
private readonly string _portName;
|
||
private bool _opened;
|
||
|
||
public event Action<byte[]>? DataReceived;
|
||
|
||
public string LogName => _portName;
|
||
|
||
/// <summary>目前傳輸速率(供 MCP 橋接列出 session 用)。</summary>
|
||
public int BaudRate => _port.BaudRate;
|
||
|
||
/// <summary>送出換行序列(AI 寫入時附加,與手打 Enter 一致)。</summary>
|
||
public string NewLine => _port.NewLine;
|
||
|
||
public SerialChannel(SerialSettings s)
|
||
{
|
||
_portName = s.PortName;
|
||
_port = new SerialPort(s.PortName, s.BaudRate, s.Parity, s.DataBits, s.StopBits)
|
||
{
|
||
Handshake = s.Handshake,
|
||
NewLine = s.NewLine
|
||
};
|
||
_port.DataReceived += OnData;
|
||
}
|
||
|
||
public void Open()
|
||
{
|
||
if (!SessionManager.TryReservePort(_portName))
|
||
throw new InvalidOperationException($"COM port {_portName} is already in use by another session.");
|
||
try
|
||
{
|
||
_port.Open();
|
||
_opened = true;
|
||
AppLogger.Info($"Serial opened: {_portName} @ {_port.BaudRate}");
|
||
}
|
||
catch
|
||
{
|
||
SessionManager.ReleasePort(_portName);
|
||
throw;
|
||
}
|
||
}
|
||
|
||
private void OnData(object sender, SerialDataReceivedEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
int n = _port.BytesToRead;
|
||
if (n <= 0) return;
|
||
var buf = new byte[n];
|
||
int read = _port.Read(buf, 0, n);
|
||
if (read > 0) DataReceived?.Invoke(read == n ? buf : buf[..read]);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
AppLogger.LogWarning($"Serial read error on {_portName}: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
public void Write(byte[] data)
|
||
{
|
||
if (_port.IsOpen) _port.Write(data, 0, data.Length);
|
||
}
|
||
|
||
// ── TTL serial 控制指令用(sendbreak / setbaud / setdtr / setrts)──
|
||
|
||
/// <summary>送出 serial break(拉 break 狀態約 300ms)。</summary>
|
||
public void SendBreak()
|
||
{
|
||
if (!_port.IsOpen) return;
|
||
try { _port.BreakState = true; Thread.Sleep(300); }
|
||
finally { try { _port.BreakState = false; } catch { } }
|
||
}
|
||
|
||
/// <summary>執行中變更 baud rate(TTL setbaud)。</summary>
|
||
public void SetBaudRate(int baud) => _port.BaudRate = baud;
|
||
|
||
public void SetDtr(bool on) => _port.DtrEnable = on;
|
||
public void SetRts(bool on) => _port.RtsEnable = on;
|
||
|
||
public void Resize(int cols, int rows) { /* serial 無 PTY size */ }
|
||
|
||
public void Close()
|
||
{
|
||
if (!_opened) return;
|
||
_opened = false;
|
||
try { if (_port.IsOpen) _port.Close(); } catch { /* 忽略關閉錯誤 */ }
|
||
SessionManager.ReleasePort(_portName);
|
||
AppLogger.Info($"Serial closed: {_portName}");
|
||
}
|
||
|
||
public void Dispose()
|
||
{
|
||
_port.DataReceived -= OnData;
|
||
Close();
|
||
_port.Dispose();
|
||
}
|
||
}
|