mirror of
https://github.com/jixishi/SerialTerminalForWindowsTerminal.git
synced 2026-06-15 16:42:46 +00:00
refactor: extract internal/config and eliminate global config var
Move Config struct to internal/config with exported fields. Replace global var config with package-level cfg pointer. Add OpenLogFile to config package. Add type alias Config = appconfig.Config in main package for backward compatibility. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
appconfig "github.com/jixishi/SerialTerminalForWindowsTerminal/internal/config"
|
||||
"github.com/jixishi/SerialTerminalForWindowsTerminal/internal/event"
|
||||
"github.com/jixishi/SerialTerminalForWindowsTerminal/pkg/charset"
|
||||
"github.com/jixishi/SerialTerminalForWindowsTerminal/pkg/forward"
|
||||
@@ -35,7 +36,7 @@ type App struct {
|
||||
}
|
||||
|
||||
func NewApp(cfg *Config) (*App, error) {
|
||||
f, err := openLogFile()
|
||||
f, err := appconfig.OpenLogFile(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -168,16 +169,16 @@ func (a *App) waitDone() <-chan struct{} {
|
||||
}
|
||||
|
||||
func (a *App) loadConfiguredForwards() {
|
||||
for i, mode := range config.forWard {
|
||||
for i, mode := range a.cfg.ForWard {
|
||||
m := forward.Mode(mode)
|
||||
if m == forward.None {
|
||||
continue
|
||||
}
|
||||
if i >= len(config.address) {
|
||||
if i >= len(a.cfg.Address) {
|
||||
a.Notifyf("[forward] skip #%d: missing address", i)
|
||||
continue
|
||||
}
|
||||
addr := strings.TrimSpace(config.address[i])
|
||||
addr := strings.TrimSpace(a.cfg.Address[i])
|
||||
if addr == "" {
|
||||
continue
|
||||
}
|
||||
@@ -192,12 +193,12 @@ func (a *App) reportForwardIngress(id int, chunk []byte) {
|
||||
return
|
||||
}
|
||||
|
||||
if strings.EqualFold(a.cfg.inputCode, "hex") {
|
||||
if strings.EqualFold(a.cfg.InputCode, "hex") {
|
||||
a.Notifyf("[forward#%d -> serial] % X\n", id, chunk)
|
||||
return
|
||||
}
|
||||
|
||||
converted, err := charset.ConvertChunk(chunk, a.cfg.inputCode, a.cfg.outputCode)
|
||||
converted, err := charset.ConvertChunk(chunk, a.cfg.InputCode, a.cfg.OutputCode)
|
||||
if err != nil {
|
||||
converted = bytes.Clone(chunk)
|
||||
}
|
||||
@@ -236,7 +237,7 @@ func (a *App) sendLine(line string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
payload := append([]byte(line), []byte(a.cfg.endStr)...)
|
||||
payload := append([]byte(line), []byte(a.cfg.EndStr)...)
|
||||
return a.writeToSession(payload)
|
||||
}
|
||||
|
||||
@@ -283,7 +284,7 @@ func (a *App) handleLine(line string) {
|
||||
}
|
||||
|
||||
func (a *App) startOutputLoop() {
|
||||
if strings.EqualFold(a.cfg.inputCode, "hex") {
|
||||
if strings.EqualFold(a.cfg.InputCode, "hex") {
|
||||
go a.readHexOutput()
|
||||
return
|
||||
}
|
||||
@@ -292,7 +293,7 @@ func (a *App) startOutputLoop() {
|
||||
}
|
||||
|
||||
func (a *App) readHexOutput() {
|
||||
frameSize := a.cfg.frameSize
|
||||
frameSize := a.cfg.FrameSize
|
||||
if frameSize <= 0 {
|
||||
frameSize = 16
|
||||
}
|
||||
@@ -312,7 +313,7 @@ func (a *App) readHexOutput() {
|
||||
if len(outChunk) == 0 {
|
||||
continue
|
||||
}
|
||||
a.emit(event.UIEvent{Kind: event.UIEventOutput, Text: charset.FormatHexFrame(outChunk, a.cfg.timesTamp, a.cfg.timesFmt)})
|
||||
a.emit(event.UIEvent{Kind: event.UIEventOutput, Text: charset.FormatHexFrame(outChunk, a.cfg.TimesTamp, a.cfg.TimesFmt)})
|
||||
}
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
@@ -347,15 +348,15 @@ func (a *App) readTextOutput() {
|
||||
continue
|
||||
}
|
||||
|
||||
converted, convErr := charset.ConvertChunk(outChunk, a.cfg.inputCode, a.cfg.outputCode)
|
||||
converted, convErr := charset.ConvertChunk(outChunk, a.cfg.InputCode, a.cfg.OutputCode)
|
||||
if convErr != nil {
|
||||
a.Notifyf("[output] convert failed: %v", convErr)
|
||||
converted = bytes.Clone(outChunk)
|
||||
}
|
||||
|
||||
text := string(converted)
|
||||
if a.cfg.timesTamp {
|
||||
text = prefixLines(text, time.Now().Format(a.cfg.timesFmt)+" ")
|
||||
if a.cfg.TimesTamp {
|
||||
text = prefixLines(text, time.Now().Format(a.cfg.TimesFmt)+" ")
|
||||
}
|
||||
a.emit(event.UIEvent{Kind: event.UIEventOutput, Text: text})
|
||||
}
|
||||
|
||||
+8
-11
@@ -61,7 +61,7 @@ func TestAppUIEvents(t *testing.T) {
|
||||
func TestSendLine(t *testing.T) {
|
||||
setupTestPipes()
|
||||
a := &App{
|
||||
cfg: &Config{endStr: "\r\n"},
|
||||
cfg: &Config{EndStr: "\r\n"},
|
||||
plugins: luaplugin.NewManager(),
|
||||
uiEvents: make(chan event.UIEvent, 8),
|
||||
done: make(chan struct{}),
|
||||
@@ -83,7 +83,7 @@ func TestSendLine(t *testing.T) {
|
||||
func TestHandleLine(t *testing.T) {
|
||||
setupTestPipes()
|
||||
a := &App{
|
||||
cfg: &Config{endStr: "\n", inputCode: "UTF-8", outputCode: "UTF-8"},
|
||||
cfg: &Config{EndStr: "\n", InputCode: "UTF-8", OutputCode: "UTF-8"},
|
||||
plugins: luaplugin.NewManager(),
|
||||
uiEvents: make(chan event.UIEvent, 8),
|
||||
done: make(chan struct{}),
|
||||
@@ -159,22 +159,19 @@ func TestAppClose(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLoadConfiguredForwards(t *testing.T) {
|
||||
oldCfg := config
|
||||
defer func() { config = oldCfg }()
|
||||
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatalf("listen failed: %v", err)
|
||||
}
|
||||
defer listener.Close()
|
||||
|
||||
config = Config{
|
||||
forWard: []int{int(forward.TCP), int(forward.None), int(forward.UDP)},
|
||||
address: []string{listener.Addr().String(), "", ""},
|
||||
testCfg := &Config{
|
||||
ForWard: []int{int(forward.TCP), int(forward.None), int(forward.UDP)},
|
||||
Address: []string{listener.Addr().String(), "", ""},
|
||||
}
|
||||
|
||||
a := &App{
|
||||
cfg: &config,
|
||||
cfg: testCfg,
|
||||
forward: forward.NewManager(func([]byte) error { return nil }, func(string, ...any) {}),
|
||||
uiEvents: make(chan event.UIEvent, 8),
|
||||
done: make(chan struct{}),
|
||||
@@ -191,7 +188,7 @@ func TestLoadConfiguredForwards(t *testing.T) {
|
||||
|
||||
func TestReportForwardIngress(t *testing.T) {
|
||||
a := &App{
|
||||
cfg: &Config{inputCode: "UTF-8", outputCode: "UTF-8"},
|
||||
cfg: &Config{InputCode: "UTF-8", OutputCode: "UTF-8"},
|
||||
uiEvents: make(chan event.UIEvent, 4),
|
||||
}
|
||||
a.SetUIEnabled(true)
|
||||
@@ -199,7 +196,7 @@ func TestReportForwardIngress(t *testing.T) {
|
||||
a.reportForwardIngress(1, []byte("test"))
|
||||
|
||||
// Hex mode
|
||||
a.cfg.inputCode = "hex"
|
||||
a.cfg.InputCode = "hex"
|
||||
a.reportForwardIngress(2, []byte{0x41, 0x42})
|
||||
|
||||
// Empty chunk
|
||||
|
||||
+13
-13
@@ -414,13 +414,13 @@ func (d *CommandDispatcher) handleModeCommand(args []string) error {
|
||||
}
|
||||
|
||||
d.app.Notifyf("[mode] input=%s output=%s end=%q hex=%v frame=%d timestamp=%v timefmt=%q forwardTargets=%d plugins=%d",
|
||||
d.app.cfg.inputCode,
|
||||
d.app.cfg.outputCode,
|
||||
d.app.cfg.endStr,
|
||||
strings.EqualFold(d.app.cfg.inputCode, "hex"),
|
||||
d.app.cfg.frameSize,
|
||||
d.app.cfg.timesTamp,
|
||||
d.app.cfg.timesFmt,
|
||||
d.app.cfg.InputCode,
|
||||
d.app.cfg.OutputCode,
|
||||
d.app.cfg.EndStr,
|
||||
strings.EqualFold(d.app.cfg.InputCode, "hex"),
|
||||
d.app.cfg.FrameSize,
|
||||
d.app.cfg.TimesTamp,
|
||||
d.app.cfg.TimesFmt,
|
||||
len(d.app.forward.List()),
|
||||
len(d.app.plugins.List()),
|
||||
)
|
||||
@@ -439,25 +439,25 @@ func (d *CommandDispatcher) handleModeCommand(args []string) error {
|
||||
|
||||
switch field {
|
||||
case "in":
|
||||
d.app.cfg.inputCode = value
|
||||
d.app.cfg.InputCode = value
|
||||
case "out":
|
||||
d.app.cfg.outputCode = value
|
||||
d.app.cfg.OutputCode = value
|
||||
case "end":
|
||||
d.app.cfg.endStr = value
|
||||
d.app.cfg.EndStr = value
|
||||
case "frame":
|
||||
n, err := strconv.Atoi(value)
|
||||
if err != nil || n <= 0 {
|
||||
return fmt.Errorf("frame must be a positive integer")
|
||||
}
|
||||
d.app.cfg.frameSize = n
|
||||
d.app.cfg.FrameSize = n
|
||||
case "timestamp":
|
||||
enabled, ok := parseOnOff(value)
|
||||
if !ok {
|
||||
return fmt.Errorf("timestamp value must be on/off")
|
||||
}
|
||||
d.app.cfg.timesTamp = enabled
|
||||
d.app.cfg.TimesTamp = enabled
|
||||
case "timefmt":
|
||||
d.app.cfg.timesFmt = value
|
||||
d.app.cfg.TimesFmt = value
|
||||
default:
|
||||
return fmt.Errorf("unknown mode field: %s", field)
|
||||
}
|
||||
|
||||
+12
-12
@@ -26,7 +26,7 @@ func setupTestPipes() {
|
||||
|
||||
func newTestAppForCommand() *App {
|
||||
a := &App{
|
||||
cfg: &Config{inputCode: "UTF-8", outputCode: "UTF-8", endStr: "\n"},
|
||||
cfg: &Config{InputCode: "UTF-8", OutputCode: "UTF-8", EndStr: "\n"},
|
||||
plugins: luaplugin.NewManager(),
|
||||
uiEvents: make(chan event.UIEvent, 32),
|
||||
done: make(chan struct{}),
|
||||
@@ -149,15 +149,15 @@ func TestCommandExecuteModeSet(t *testing.T) {
|
||||
if err != nil || !handled {
|
||||
t.Fatalf(".mode set end failed handled=%v err=%v", handled, err)
|
||||
}
|
||||
if a.cfg.endStr != "\\r\\n" {
|
||||
t.Fatalf("mode set end not applied, got=%q", a.cfg.endStr)
|
||||
if a.cfg.EndStr != "\\r\\n" {
|
||||
t.Fatalf("mode set end not applied, got=%q", a.cfg.EndStr)
|
||||
}
|
||||
|
||||
handled, err = a.dispatcher.Execute(".mode set timestamp on")
|
||||
if err != nil || !handled {
|
||||
t.Fatalf(".mode set timestamp failed handled=%v err=%v", handled, err)
|
||||
}
|
||||
if !a.cfg.timesTamp {
|
||||
if !a.cfg.TimesTamp {
|
||||
t.Fatalf("mode set timestamp should enable timesTamp")
|
||||
}
|
||||
}
|
||||
@@ -298,32 +298,32 @@ func TestCommandExecuteModeSetAll(t *testing.T) {
|
||||
if err != nil || !handled {
|
||||
t.Fatalf(".mode set frame failed: handled=%v err=%v", handled, err)
|
||||
}
|
||||
if a.cfg.frameSize != 32 {
|
||||
t.Fatalf("frameSize not set, got=%d", a.cfg.frameSize)
|
||||
if a.cfg.FrameSize != 32 {
|
||||
t.Fatalf("frameSize not set, got=%d", a.cfg.FrameSize)
|
||||
}
|
||||
|
||||
handled, err = a.dispatcher.Execute(".mode set timefmt 2006")
|
||||
if err != nil || !handled {
|
||||
t.Fatalf(".mode set timefmt failed: handled=%v err=%v", handled, err)
|
||||
}
|
||||
if a.cfg.timesFmt != "2006" {
|
||||
t.Fatalf("timesFmt not set, got=%q", a.cfg.timesFmt)
|
||||
if a.cfg.TimesFmt != "2006" {
|
||||
t.Fatalf("timesFmt not set, got=%q", a.cfg.TimesFmt)
|
||||
}
|
||||
|
||||
handled, err = a.dispatcher.Execute(".mode set out GBK")
|
||||
if err != nil || !handled {
|
||||
t.Fatalf(".mode set out failed: handled=%v err=%v", handled, err)
|
||||
}
|
||||
if a.cfg.outputCode != "GBK" {
|
||||
t.Fatalf("outputCode not set, got=%q", a.cfg.outputCode)
|
||||
if a.cfg.OutputCode != "GBK" {
|
||||
t.Fatalf("outputCode not set, got=%q", a.cfg.OutputCode)
|
||||
}
|
||||
|
||||
handled, err = a.dispatcher.Execute(".mode set in GBK")
|
||||
if err != nil || !handled {
|
||||
t.Fatalf(".mode set in failed: handled=%v err=%v", handled, err)
|
||||
}
|
||||
if a.cfg.inputCode != "GBK" {
|
||||
t.Fatalf("inputCode not set, got=%q", a.cfg.inputCode)
|
||||
if a.cfg.InputCode != "GBK" {
|
||||
t.Fatalf("inputCode not set, got=%q", a.cfg.InputCode)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,42 +1,10 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
appconfig "github.com/jixishi/SerialTerminalForWindowsTerminal/internal/config"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
portName string
|
||||
baudRate int
|
||||
dataBits int
|
||||
stopBits int
|
||||
parityBit int
|
||||
outputCode string
|
||||
inputCode string
|
||||
endStr string
|
||||
enableLog bool
|
||||
logFilePath string
|
||||
forWard []int
|
||||
frameSize int
|
||||
timesTamp bool
|
||||
timesFmt string
|
||||
address []string
|
||||
enableGUI bool
|
||||
hotkeyMod string
|
||||
}
|
||||
// Config is an alias for appconfig.Config to keep main-package code concise.
|
||||
type Config = appconfig.Config
|
||||
|
||||
var config Config
|
||||
|
||||
func openLogFile() (*os.File, error) {
|
||||
if config.enableLog {
|
||||
path := fmt.Sprintf(config.logFilePath, config.portName, time.Now().Format("2006_01_02T150405"))
|
||||
f, err := os.OpenFile(path, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
var cfg = &Config{}
|
||||
|
||||
+31
-30
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
appconfig "github.com/jixishi/SerialTerminalForWindowsTerminal/internal/config"
|
||||
"github.com/jixishi/SerialTerminalForWindowsTerminal/pkg/forward"
|
||||
)
|
||||
|
||||
@@ -55,16 +56,16 @@ func TestParseForwardMode(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestOpenLogFile(t *testing.T) {
|
||||
old := config
|
||||
defer func() { config = old }()
|
||||
old := *cfg
|
||||
defer func() { *cfg = old }()
|
||||
|
||||
config = Config{
|
||||
enableLog: true,
|
||||
portName: "COM1",
|
||||
logFilePath: filepath.Join(t.TempDir(), "%s-%s.log"),
|
||||
*cfg = Config{
|
||||
EnableLog: true,
|
||||
PortName: "COM1",
|
||||
LogFilePath: filepath.Join(t.TempDir(), "%s-%s.log"),
|
||||
}
|
||||
|
||||
f, err := openLogFile()
|
||||
f, err := appconfig.OpenLogFile(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("openLogFile() unexpected error: %v", err)
|
||||
}
|
||||
@@ -73,8 +74,8 @@ func TestOpenLogFile(t *testing.T) {
|
||||
}
|
||||
_ = f.Close()
|
||||
|
||||
config.enableLog = false
|
||||
f, err = openLogFile()
|
||||
cfg.EnableLog = false
|
||||
f, err = appconfig.OpenLogFile(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("openLogFile() unexpected error with enableLog=false: %v", err)
|
||||
}
|
||||
@@ -114,55 +115,55 @@ func TestFlagFindValue(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestFlagExt(t *testing.T) {
|
||||
old := config
|
||||
defer func() { config = old }()
|
||||
old := *cfg
|
||||
defer func() { *cfg = old }()
|
||||
|
||||
config = Config{}
|
||||
*cfg = Config{}
|
||||
flagExt()
|
||||
if config.enableLog {
|
||||
if cfg.EnableLog {
|
||||
t.Fatalf("expected enableLog=false when logFilePath empty")
|
||||
}
|
||||
if config.timesTamp {
|
||||
if cfg.TimesTamp {
|
||||
t.Fatalf("expected timesTamp=false when timesFmt empty")
|
||||
}
|
||||
if config.hotkeyMod != "ctrl+alt" {
|
||||
t.Fatalf("expected default hotkeyMod=ctrl+alt, got=%q", config.hotkeyMod)
|
||||
if cfg.HotkeyMod != "ctrl+alt" {
|
||||
t.Fatalf("expected default hotkeyMod=ctrl+alt, got=%q", cfg.HotkeyMod)
|
||||
}
|
||||
|
||||
config = Config{logFilePath: "/tmp/log.txt"}
|
||||
*cfg = Config{LogFilePath: "/tmp/log.txt"}
|
||||
flagExt()
|
||||
if !config.enableLog {
|
||||
if !cfg.EnableLog {
|
||||
t.Fatalf("expected enableLog=true when logFilePath set")
|
||||
}
|
||||
|
||||
config = Config{timesFmt: "2006-01-02"}
|
||||
*cfg = Config{TimesFmt: "2006-01-02"}
|
||||
flagExt()
|
||||
if !config.timesTamp {
|
||||
if !cfg.TimesTamp {
|
||||
t.Fatalf("expected timesTamp=true when timesFmt set")
|
||||
}
|
||||
|
||||
config = Config{hotkeyMod: ""}
|
||||
*cfg = Config{HotkeyMod: ""}
|
||||
flagExt()
|
||||
if config.hotkeyMod != "ctrl+alt" {
|
||||
if cfg.HotkeyMod != "ctrl+alt" {
|
||||
t.Fatalf("empty hotkeyMod should default to ctrl+alt")
|
||||
}
|
||||
|
||||
config = Config{hotkeyMod: "ctrl+shift"}
|
||||
*cfg = Config{HotkeyMod: "ctrl+shift"}
|
||||
flagExt()
|
||||
if config.hotkeyMod != "ctrl+shift" {
|
||||
if cfg.HotkeyMod != "ctrl+shift" {
|
||||
t.Fatalf("expected ctrl+shift preserved")
|
||||
}
|
||||
|
||||
config = Config{hotkeyMod: " CTRL+SHIFT "}
|
||||
*cfg = Config{HotkeyMod: " CTRL+SHIFT "}
|
||||
flagExt()
|
||||
if config.hotkeyMod != "ctrl+shift" {
|
||||
t.Fatalf("expected whitespace+case normalization, got=%q", config.hotkeyMod)
|
||||
if cfg.HotkeyMod != "ctrl+shift" {
|
||||
t.Fatalf("expected whitespace+case normalization, got=%q", cfg.HotkeyMod)
|
||||
}
|
||||
|
||||
config = Config{hotkeyMod: "invalid"}
|
||||
*cfg = Config{HotkeyMod: "invalid"}
|
||||
flagExt()
|
||||
if config.hotkeyMod != "ctrl+alt" {
|
||||
t.Fatalf("invalid hotkeyMod should default to ctrl+alt, got=%q", config.hotkeyMod)
|
||||
if cfg.HotkeyMod != "ctrl+alt" {
|
||||
t.Fatalf("invalid hotkeyMod should default to ctrl+alt, got=%q", cfg.HotkeyMod)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -60,10 +60,10 @@ func TestParseCSIu(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIsExitHotkeySeq(t *testing.T) {
|
||||
oldCfg := config
|
||||
defer func() { config = oldCfg }()
|
||||
oldCfg := *cfg
|
||||
defer func() { *cfg = oldCfg }()
|
||||
|
||||
config = Config{hotkeyMod: "ctrl+alt"}
|
||||
*cfg = Config{HotkeyMod: "ctrl+alt"}
|
||||
|
||||
// CSI u Ctrl+Alt+C (mod=6)
|
||||
if !isExitHotkeySeq([]byte{0x1b, '[', '9', '9', ';', '6', 'u'}) {
|
||||
@@ -88,7 +88,7 @@ func TestIsExitHotkeySeq(t *testing.T) {
|
||||
}
|
||||
|
||||
// Switch to ctrl+shift
|
||||
config = Config{hotkeyMod: "ctrl+shift"}
|
||||
*cfg = Config{HotkeyMod: "ctrl+shift"}
|
||||
|
||||
if !isExitHotkeySeq([]byte{0x1b, '[', '9', '9', ';', '5', 'u'}) {
|
||||
t.Fatalf("Ctrl+Shift+C should exit with ctrl+shift config")
|
||||
@@ -111,7 +111,7 @@ func TestIsExitHotkeySeq(t *testing.T) {
|
||||
t.Fatalf("plain bytes should not exit")
|
||||
}
|
||||
|
||||
config = Config{hotkeyMod: "ctrl+alt"}
|
||||
*cfg = Config{HotkeyMod: "ctrl+alt"}
|
||||
// Ctrl only (mod=4) should not exit (requires Alt too)
|
||||
if isExitHotkeySeq([]byte{0x1b, '[', '9', '9', ';', '4', 'u'}) {
|
||||
t.Fatalf("Ctrl+C (without Alt) should not exit")
|
||||
|
||||
@@ -47,21 +47,21 @@ type Flag struct {
|
||||
}
|
||||
|
||||
var (
|
||||
portName = Flag{ptrVal{string: &config.portName}, "p", "port", Val{string: ""}, "要连接的串口\t(/dev/ttyUSB0、COMx)"}
|
||||
baudRate = Flag{ptrVal{int: &config.baudRate}, "b", "baud", Val{int: 115200}, "波特率"}
|
||||
dataBits = Flag{ptrVal{int: &config.dataBits}, "d", "data", Val{int: 8}, "数据位"}
|
||||
stopBits = Flag{ptrVal{int: &config.stopBits}, "s", "stop", Val{int: 0}, "停止位停止位(0: 1停止 1:1.5停止 2:2停止)"}
|
||||
outputCode = Flag{ptrVal{string: &config.outputCode}, "o", "out", Val{string: "UTF-8"}, "输出编码"}
|
||||
inputCode = Flag{ptrVal{string: &config.inputCode}, "i", "in", Val{string: "UTF-8"}, "输入编码"}
|
||||
endStr = Flag{ptrVal{string: &config.endStr}, "e", "end", Val{string: "\n"}, "终端换行符"}
|
||||
logExt = Flag{v: ptrVal{ext: &config.logFilePath}, sStr: "l", lStr: "log", dv: Val{extdef: "./%s-$s.txt", string: ""}, help: "日志保存路径"}
|
||||
timeExt = Flag{v: ptrVal{ext: &config.timesFmt}, sStr: "t", lStr: "time", dv: Val{extdef: "[06-01-02 15:04:05.000]", string: ""}, help: "时间戳格式化字段"}
|
||||
forWard = Flag{ptrVal{il: &config.forWard}, "f", "forward", Val{int: 0}, "转发模式(0: 无 1:TCP-C 2:UDP-C 支持多次传入)"}
|
||||
address = Flag{ptrVal{sl: &config.address}, "a", "address", Val{string: "127.0.0.1:12345"}, "转发服务地址(支持多次传入)"}
|
||||
frameSize = Flag{ptrVal{int: &config.frameSize}, "F", "Frame", Val{int: 16}, "帧大小"}
|
||||
parityBit = Flag{ptrVal{int: &config.parityBit}, "v", "verify", Val{int: 0}, "奇偶校验(0:无校验、1:奇校验、2:偶校验、3:1校验、4:0校验)"}
|
||||
guiMode = Flag{ptrVal{bool: &config.enableGUI}, "g", "gui", Val{bool: false}, "启用TUI交互界面"}
|
||||
hotkeyMod = Flag{ptrVal{string: &config.hotkeyMod}, "k", "hotkey-mod", Val{string: "ctrl+alt"}, "本地快捷键修饰(ctrl+alt|ctrl+shift)"}
|
||||
portName = Flag{ptrVal{string: &cfg.PortName}, "p", "port", Val{string: ""}, "要连接的串口\t(/dev/ttyUSB0、COMx)"}
|
||||
baudRate = Flag{ptrVal{int: &cfg.BaudRate}, "b", "baud", Val{int: 115200}, "波特率"}
|
||||
dataBits = Flag{ptrVal{int: &cfg.DataBits}, "d", "data", Val{int: 8}, "数据位"}
|
||||
stopBits = Flag{ptrVal{int: &cfg.StopBits}, "s", "stop", Val{int: 0}, "停止位停止位(0: 1停止 1:1.5停止 2:2停止)"}
|
||||
outputCode = Flag{ptrVal{string: &cfg.OutputCode}, "o", "out", Val{string: "UTF-8"}, "输出编码"}
|
||||
inputCode = Flag{ptrVal{string: &cfg.InputCode}, "i", "in", Val{string: "UTF-8"}, "输入编码"}
|
||||
endStr = Flag{ptrVal{string: &cfg.EndStr}, "e", "end", Val{string: "\n"}, "终端换行符"}
|
||||
logExt = Flag{v: ptrVal{ext: &cfg.LogFilePath}, sStr: "l", lStr: "log", dv: Val{extdef: "./%s-$s.txt", string: ""}, help: "日志保存路径"}
|
||||
timeExt = Flag{v: ptrVal{ext: &cfg.TimesFmt}, sStr: "t", lStr: "time", dv: Val{extdef: "[06-01-02 15:04:05.000]", string: ""}, help: "时间戳格式化字段"}
|
||||
forWard = Flag{ptrVal{il: &cfg.ForWard}, "f", "forward", Val{int: 0}, "转发模式(0: 无 1:TCP-C 2:UDP-C 支持多次传入)"}
|
||||
address = Flag{ptrVal{sl: &cfg.Address}, "a", "address", Val{string: "127.0.0.1:12345"}, "转发服务地址(支持多次传入)"}
|
||||
frameSize = Flag{ptrVal{int: &cfg.FrameSize}, "F", "Frame", Val{int: 16}, "帧大小"}
|
||||
parityBit = Flag{ptrVal{int: &cfg.ParityBit}, "v", "verify", Val{int: 0}, "奇偶校验(0:无校验、1:奇校验、2:偶校验、3:1校验、4:0校验)"}
|
||||
guiMode = Flag{ptrVal{bool: &cfg.EnableGUI}, "g", "gui", Val{bool: false}, "启用TUI交互界面"}
|
||||
hotkeyMod = Flag{ptrVal{string: &cfg.HotkeyMod}, "k", "hotkey-mod", Val{string: "ctrl+alt"}, "本地快捷键修饰(ctrl+alt|ctrl+shift)"}
|
||||
flags = []Flag{portName, baudRate, dataBits, stopBits, outputCode, inputCode, endStr, forWard, address, frameSize, parityBit, logExt, timeExt, guiMode, hotkeyMod}
|
||||
)
|
||||
|
||||
@@ -182,18 +182,18 @@ func flagInit(f *Flag) {
|
||||
}
|
||||
}
|
||||
func flagExt() {
|
||||
if config.logFilePath != "" {
|
||||
config.enableLog = true
|
||||
if cfg.LogFilePath != "" {
|
||||
cfg.EnableLog = true
|
||||
}
|
||||
if config.timesFmt != "" {
|
||||
config.timesTamp = true
|
||||
if cfg.TimesFmt != "" {
|
||||
cfg.TimesTamp = true
|
||||
}
|
||||
if config.hotkeyMod == "" {
|
||||
config.hotkeyMod = "ctrl+alt"
|
||||
if cfg.HotkeyMod == "" {
|
||||
cfg.HotkeyMod = "ctrl+alt"
|
||||
}
|
||||
config.hotkeyMod = strings.ToLower(strings.TrimSpace(config.hotkeyMod))
|
||||
if config.hotkeyMod != "ctrl+alt" && config.hotkeyMod != "ctrl+shift" {
|
||||
config.hotkeyMod = "ctrl+alt"
|
||||
cfg.HotkeyMod = strings.ToLower(strings.TrimSpace(cfg.HotkeyMod))
|
||||
if cfg.HotkeyMod != "ctrl+alt" && cfg.HotkeyMod != "ctrl+shift" {
|
||||
cfg.HotkeyMod = "ctrl+alt"
|
||||
}
|
||||
}
|
||||
func getCliFlag() {
|
||||
@@ -230,7 +230,7 @@ func getCliFlag() {
|
||||
singleselect.WithPageSize(4),
|
||||
singleselect.WithFilterInput(inputs),
|
||||
).Display("选择串口")
|
||||
config.portName = ports[s]
|
||||
cfg.PortName = ports[s]
|
||||
|
||||
s, _ = inf.NewSingleSelect(
|
||||
bauds,
|
||||
@@ -238,38 +238,38 @@ func getCliFlag() {
|
||||
singleselect.WithPageSize(4),
|
||||
).Display("选择波特率")
|
||||
if s != 0 {
|
||||
config.baudRate, _ = strconv.Atoi(bauds[s])
|
||||
cfg.BaudRate, _ = strconv.Atoi(bauds[s])
|
||||
} else {
|
||||
b, _ := inf.NewText(
|
||||
text.WithPrompt("BaudRate:"),
|
||||
text.WithPromptStyle(theme.DefaultTheme.PromptStyle),
|
||||
text.WithDefaultValue("115200"),
|
||||
).Display()
|
||||
config.baudRate, _ = strconv.Atoi(b)
|
||||
cfg.BaudRate, _ = strconv.Atoi(b)
|
||||
}
|
||||
v, _ := inf.NewConfirmWithSelection(
|
||||
confirm.WithPrompt("启用Hex"),
|
||||
).Display()
|
||||
if v {
|
||||
config.inputCode = "hex"
|
||||
cfg.InputCode = "hex"
|
||||
b, _ := inf.NewText(
|
||||
text.WithPrompt("Frames:"),
|
||||
text.WithPromptStyle(theme.DefaultTheme.PromptStyle),
|
||||
text.WithDefaultValue("16"),
|
||||
).Display()
|
||||
config.frameSize, _ = strconv.Atoi(b)
|
||||
cfg.FrameSize, _ = strconv.Atoi(b)
|
||||
}
|
||||
v, _ = inf.NewConfirmWithSelection(
|
||||
confirm.WithPrompt("启用时间戳"),
|
||||
).Display()
|
||||
config.timesTamp = v
|
||||
cfg.TimesTamp = v
|
||||
if v {
|
||||
b, _ := inf.NewText(
|
||||
text.WithPrompt("格式化字段:"),
|
||||
text.WithPromptStyle(theme.DefaultTheme.PromptStyle),
|
||||
text.WithDefaultValue(timeExt.dv.extdef),
|
||||
).Display()
|
||||
config.timesFmt = b
|
||||
cfg.TimesFmt = b
|
||||
}
|
||||
v, _ = inf.NewConfirmWithSelection(
|
||||
confirm.WithPrompt("启用高级配置"),
|
||||
@@ -281,7 +281,7 @@ func getCliFlag() {
|
||||
singleselect.WithPageSize(4),
|
||||
singleselect.WithFilterInput(inputs),
|
||||
).Display("选择数据位")
|
||||
config.dataBits, _ = strconv.Atoi(datas[s])
|
||||
cfg.DataBits, _ = strconv.Atoi(datas[s])
|
||||
|
||||
s, _ = inf.NewSingleSelect(
|
||||
stops,
|
||||
@@ -289,7 +289,7 @@ func getCliFlag() {
|
||||
singleselect.WithPageSize(4),
|
||||
singleselect.WithFilterInput(inputs),
|
||||
).Display("选择停止位")
|
||||
config.stopBits = s
|
||||
cfg.StopBits = s
|
||||
|
||||
s, _ = inf.NewSingleSelect(
|
||||
paritys,
|
||||
@@ -297,14 +297,14 @@ func getCliFlag() {
|
||||
singleselect.WithPageSize(4),
|
||||
singleselect.WithFilterInput(inputs),
|
||||
).Display("选择校验位")
|
||||
config.parityBit = s
|
||||
cfg.ParityBit = s
|
||||
|
||||
t, _ := inf.NewText(
|
||||
text.WithPrompt("换行符:"),
|
||||
text.WithPromptStyle(theme.DefaultTheme.PromptStyle),
|
||||
text.WithDefaultValue(endStr.dv.string),
|
||||
).Display()
|
||||
config.endStr = t
|
||||
cfg.EndStr = t
|
||||
|
||||
v, _ = inf.NewConfirmWithSelection(
|
||||
confirm.WithDefaultYes(),
|
||||
@@ -317,14 +317,14 @@ func getCliFlag() {
|
||||
text.WithPromptStyle(theme.DefaultTheme.PromptStyle),
|
||||
text.WithDefaultValue(inputCode.dv.string),
|
||||
).Display()
|
||||
config.inputCode = t
|
||||
cfg.InputCode = t
|
||||
|
||||
t, _ = inf.NewText(
|
||||
text.WithPrompt("输出编码:"),
|
||||
text.WithPromptStyle(theme.DefaultTheme.PromptStyle),
|
||||
text.WithDefaultValue(outputCode.dv.string),
|
||||
).Display()
|
||||
config.outputCode = t
|
||||
cfg.OutputCode = t
|
||||
}
|
||||
G_F_mode:
|
||||
s, _ = inf.NewSingleSelect(
|
||||
@@ -334,13 +334,13 @@ func getCliFlag() {
|
||||
singleselect.WithFilterInput(inputs),
|
||||
).Display("选择转发模式")
|
||||
if s != 0 {
|
||||
config.forWard = append(config.forWard, s)
|
||||
cfg.ForWard = append(cfg.ForWard, s)
|
||||
t, _ = inf.NewText(
|
||||
text.WithPrompt("地址:"),
|
||||
text.WithPromptStyle(theme.DefaultTheme.PromptStyle),
|
||||
text.WithDefaultValue(address.dv.string),
|
||||
).Display()
|
||||
config.address = append(config.address, t)
|
||||
cfg.Address = append(cfg.Address, t)
|
||||
goto G_F_mode
|
||||
}
|
||||
|
||||
@@ -348,14 +348,14 @@ func getCliFlag() {
|
||||
confirm.WithDefaultYes(),
|
||||
confirm.WithPrompt("启用日志"),
|
||||
).Display()
|
||||
config.enableLog = e
|
||||
cfg.EnableLog = e
|
||||
if e {
|
||||
t, _ = inf.NewText(
|
||||
text.WithPrompt("Path:"),
|
||||
text.WithPromptStyle(theme.DefaultTheme.PromptStyle),
|
||||
text.WithDefaultValue("./%s-$s.txt"),
|
||||
).Display()
|
||||
config.logFilePath = t
|
||||
cfg.LogFilePath = t
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
// Package config holds the application configuration.
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Config holds all application settings.
|
||||
type Config struct {
|
||||
PortName string
|
||||
BaudRate int
|
||||
DataBits int
|
||||
StopBits int
|
||||
ParityBit int
|
||||
OutputCode string
|
||||
InputCode string
|
||||
EndStr string
|
||||
EnableLog bool
|
||||
LogFilePath string
|
||||
ForWard []int
|
||||
FrameSize int
|
||||
TimesTamp bool
|
||||
TimesFmt string
|
||||
Address []string
|
||||
EnableGUI bool
|
||||
HotkeyMod string
|
||||
}
|
||||
|
||||
// OpenLogFile opens the configured log file for writing, or returns nil if logging is disabled.
|
||||
func OpenLogFile(cfg *Config) (*os.File, error) {
|
||||
if cfg.EnableLog {
|
||||
path := fmt.Sprintf(cfg.LogFilePath, cfg.PortName, time.Now().Format("2006_01_02T150405"))
|
||||
f, err := os.OpenFile(path, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
@@ -32,10 +32,10 @@ func main() {
|
||||
normalizeFlags()
|
||||
pflag.Parse()
|
||||
flagExt()
|
||||
if config.portName == "" {
|
||||
if cfg.PortName == "" {
|
||||
getCliFlag()
|
||||
}
|
||||
ports, err := checkPortAvailability(config.portName)
|
||||
ports, err := checkPortAvailability(cfg.PortName)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
printUsage(ports)
|
||||
@@ -52,7 +52,7 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
app, err := NewApp(&config)
|
||||
app, err := NewApp(cfg)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "create app failed: %v\n", err)
|
||||
os.Exit(1)
|
||||
@@ -63,9 +63,9 @@ func main() {
|
||||
app.startOutputLoop()
|
||||
|
||||
go forwardInterruptToRemote(app)
|
||||
app.SetUIEnabled(config.enableGUI)
|
||||
app.SetUIEnabled(cfg.EnableGUI)
|
||||
|
||||
if config.enableGUI {
|
||||
if cfg.EnableGUI {
|
||||
model := newUIModel(app)
|
||||
p := tea.NewProgram(model, tea.WithAltScreen(), tea.WithoutSignalHandler())
|
||||
if _, err = p.Run(); err != nil {
|
||||
@@ -287,7 +287,7 @@ func runConsole(app *App) error {
|
||||
}
|
||||
|
||||
if b == '\r' || b == '\n' {
|
||||
if err = app.writeToSession([]byte(config.endStr)); err != nil {
|
||||
if err = app.writeToSession([]byte(cfg.EndStr)); err != nil {
|
||||
app.Statusf("[send] %v", err)
|
||||
}
|
||||
lineStart = true
|
||||
@@ -328,7 +328,7 @@ func parseCSIu(seq []byte) (cp int, mod int, ok bool) {
|
||||
}
|
||||
|
||||
func isAltKeyExit(b byte) bool {
|
||||
if normalizeHotkeyPrefix(config.hotkeyMod) != "ctrl+alt" {
|
||||
if normalizeHotkeyPrefix(cfg.HotkeyMod) != "ctrl+alt" {
|
||||
return false
|
||||
}
|
||||
// 0x2E = scan code for 'C', 0x03 = Ctrl+C, 0x63 = 'c', 0x43 = 'C'
|
||||
@@ -336,7 +336,7 @@ func isAltKeyExit(b byte) bool {
|
||||
}
|
||||
|
||||
func isExitHotkeySeq(seq []byte) bool {
|
||||
mod := normalizeHotkeyPrefix(config.hotkeyMod)
|
||||
mod := normalizeHotkeyPrefix(cfg.HotkeyMod)
|
||||
|
||||
// CSI u format: ESC [ codepoint ; modifier u
|
||||
// Only matches when the Ctrl modifier bit (4) is present,
|
||||
|
||||
+2
-2
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
func handleLocalHotkey(m *uiModel, key string) bool {
|
||||
if m.isLocalHotkey(key, "h") {
|
||||
modifier := strings.ToUpper(normalizeHotkeyPrefix(m.app.cfg.hotkeyMod))
|
||||
modifier := strings.ToUpper(normalizeHotkeyPrefix(m.app.cfg.HotkeyMod))
|
||||
m.app.ShowModal("Shortcuts", modifier+"+C => local exit\nCtrl+C => remote interrupt\n"+modifier+"+F => forward panel\n"+modifier+"+P => plugin panel\n"+modifier+"+M => mode panel\nF1 => shortcut help")
|
||||
return true
|
||||
}
|
||||
@@ -48,7 +48,7 @@ func (m *uiModel) isLocalHotkey(key, action string) bool {
|
||||
}
|
||||
}
|
||||
|
||||
mod := normalizeHotkeyPrefix(m.app.cfg.hotkeyMod)
|
||||
mod := normalizeHotkeyPrefix(m.app.cfg.HotkeyMod)
|
||||
if mod == "ctrl+shift" {
|
||||
return hasCtrl && hasShift
|
||||
}
|
||||
|
||||
+4
-4
@@ -160,7 +160,7 @@ func (m *uiModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
}
|
||||
|
||||
if m.isLocalHotkey(keyStr, "c") {
|
||||
m.app.Statusf("[local] exiting by %s+C", strings.ToUpper(normalizeHotkeyPrefix(m.app.cfg.hotkeyMod)))
|
||||
m.app.Statusf("[local] exiting by %s+C", strings.ToUpper(normalizeHotkeyPrefix(m.app.cfg.HotkeyMod)))
|
||||
m.app.Close()
|
||||
return m, tea.Quit
|
||||
}
|
||||
@@ -171,7 +171,7 @@ func (m *uiModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
|
||||
// Some terminals can't encode Ctrl+Alt/Shift+H distinctly and report Ctrl+H.
|
||||
if keyStr == "ctrl+h" {
|
||||
handleLocalHotkey(m, hotkeyWith(m.app.cfg.hotkeyMod, "h"))
|
||||
handleLocalHotkey(m, hotkeyWith(m.app.cfg.HotkeyMod, "h"))
|
||||
return m, nil
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ func (m *uiModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
|
||||
switch keyStr {
|
||||
case "f1":
|
||||
handleLocalHotkey(m, hotkeyWith(m.app.cfg.hotkeyMod, "h"))
|
||||
handleLocalHotkey(m, hotkeyWith(m.app.cfg.HotkeyMod, "h"))
|
||||
return m, nil
|
||||
|
||||
case "tab", "shift+tab":
|
||||
@@ -245,7 +245,7 @@ func (m *uiModel) View() string {
|
||||
} else if len(m.suggestions) == 1 {
|
||||
suggest = "Tab: " + m.suggestions[0]
|
||||
}
|
||||
modifier := strings.ToUpper(normalizeHotkeyPrefix(m.app.cfg.hotkeyMod))
|
||||
modifier := strings.ToUpper(normalizeHotkeyPrefix(m.app.cfg.HotkeyMod))
|
||||
hotkeys := "Hotkeys: Ctrl+C remote | " + modifier + "+C local | " + modifier + "+F forward | " + modifier + "+P plugins | " + modifier + "+M mode | F1 help"
|
||||
hotkeys = lipgloss.NewStyle().Faint(true).Foreground(lipgloss.Color("245")).Render(hotkeys)
|
||||
status := m.statusLine
|
||||
|
||||
+2
-2
@@ -71,7 +71,7 @@ func (m *uiModel) refreshPanel() {
|
||||
}
|
||||
|
||||
func (m *uiModel) buildModeItems() []modeItem {
|
||||
return []modeItem{{"in", "Input Charset", m.app.cfg.inputCode}, {"out", "Output Charset", m.app.cfg.outputCode}, {"end", "Line End", fmt.Sprintf("%q", m.app.cfg.endStr)}, {"frame", "Hex Frame Size", fmt.Sprintf("%d", m.app.cfg.frameSize)}, {"timestamp", "Timestamp", fmt.Sprintf("%v", m.app.cfg.timesTamp)}, {"timefmt", "Timestamp Format", m.app.cfg.timesFmt}}
|
||||
return []modeItem{{"in", "Input Charset", m.app.cfg.InputCode}, {"out", "Output Charset", m.app.cfg.OutputCode}, {"end", "Line End", fmt.Sprintf("%q", m.app.cfg.EndStr)}, {"frame", "Hex Frame Size", fmt.Sprintf("%d", m.app.cfg.FrameSize)}, {"timestamp", "Timestamp", fmt.Sprintf("%v", m.app.cfg.TimesTamp)}, {"timefmt", "Timestamp Format", m.app.cfg.TimesFmt}}
|
||||
}
|
||||
|
||||
func (m *uiModel) handleForwardPanelKey(key string) bool {
|
||||
@@ -213,7 +213,7 @@ func (m *uiModel) handleModePanelKey(key string) bool {
|
||||
switch key {
|
||||
case " ":
|
||||
if sel.key == "timestamp" {
|
||||
if m.app.cfg.timesTamp {
|
||||
if m.app.cfg.TimesTamp {
|
||||
m.app.handleLine(".mode set timestamp off")
|
||||
} else {
|
||||
m.app.handleLine(".mode set timestamp on")
|
||||
|
||||
+7
-7
@@ -42,7 +42,7 @@ func TestRenderModal(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHandleCtrlShiftLocalHelp(t *testing.T) {
|
||||
a := &App{uiEvents: make(chan event.UIEvent, 4), cfg: &Config{hotkeyMod: "ctrl+alt"}}
|
||||
a := &App{uiEvents: make(chan event.UIEvent, 4), cfg: &Config{HotkeyMod: "ctrl+alt"}}
|
||||
a.SetUIEnabled(true)
|
||||
m := uiModel{app: a}
|
||||
|
||||
@@ -103,7 +103,7 @@ func TestIsLocalHotkeyAll(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
a := &App{cfg: &Config{hotkeyMod: tt.mod}}
|
||||
a := &App{cfg: &Config{HotkeyMod: tt.mod}}
|
||||
m := uiModel{app: a}
|
||||
got := m.isLocalHotkey(tt.key, tt.action)
|
||||
if got != tt.want {
|
||||
@@ -216,7 +216,7 @@ func TestMaxIntFunc(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHandleLocalHotkeyForward(t *testing.T) {
|
||||
a := &App{uiEvents: make(chan event.UIEvent, 4), cfg: &Config{hotkeyMod: "ctrl+alt"}}
|
||||
a := &App{uiEvents: make(chan event.UIEvent, 4), cfg: &Config{HotkeyMod: "ctrl+alt"}}
|
||||
a.SetUIEnabled(true)
|
||||
m := uiModel{app: a}
|
||||
|
||||
@@ -230,7 +230,7 @@ func TestHandleLocalHotkeyForward(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHandleLocalHotkeyPlugin(t *testing.T) {
|
||||
a := &App{uiEvents: make(chan event.UIEvent, 4), cfg: &Config{hotkeyMod: "ctrl+alt"}}
|
||||
a := &App{uiEvents: make(chan event.UIEvent, 4), cfg: &Config{HotkeyMod: "ctrl+alt"}}
|
||||
a.SetUIEnabled(true)
|
||||
m := uiModel{app: a}
|
||||
|
||||
@@ -244,7 +244,7 @@ func TestHandleLocalHotkeyPlugin(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHandleLocalHotkeyMode(t *testing.T) {
|
||||
a := &App{uiEvents: make(chan event.UIEvent, 4), cfg: &Config{hotkeyMod: "ctrl+alt"}}
|
||||
a := &App{uiEvents: make(chan event.UIEvent, 4), cfg: &Config{HotkeyMod: "ctrl+alt"}}
|
||||
a.SetUIEnabled(true)
|
||||
m := uiModel{app: a}
|
||||
|
||||
@@ -258,7 +258,7 @@ func TestHandleLocalHotkeyMode(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHandleLocalHotkeyUnknown(t *testing.T) {
|
||||
a := &App{cfg: &Config{hotkeyMod: "ctrl+alt"}}
|
||||
a := &App{cfg: &Config{HotkeyMod: "ctrl+alt"}}
|
||||
m := uiModel{app: a}
|
||||
|
||||
if handleLocalHotkey(&m, "ctrl+alt+x") {
|
||||
@@ -267,7 +267,7 @@ func TestHandleLocalHotkeyUnknown(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHandleLocalHotkeyCtrlShift(t *testing.T) {
|
||||
a := &App{uiEvents: make(chan event.UIEvent, 4), cfg: &Config{hotkeyMod: "ctrl+shift"}}
|
||||
a := &App{uiEvents: make(chan event.UIEvent, 4), cfg: &Config{HotkeyMod: "ctrl+shift"}}
|
||||
a.SetUIEnabled(true)
|
||||
m := uiModel{app: a}
|
||||
|
||||
|
||||
@@ -34,13 +34,13 @@ func checkPortAvailability(name string) ([]string, error) {
|
||||
|
||||
func OpenSerial() error {
|
||||
mode := &serial.Mode{
|
||||
BaudRate: config.baudRate,
|
||||
StopBits: serial.StopBits(config.stopBits),
|
||||
DataBits: config.dataBits,
|
||||
Parity: serial.Parity(config.parityBit),
|
||||
BaudRate: cfg.BaudRate,
|
||||
StopBits: serial.StopBits(cfg.StopBits),
|
||||
DataBits: cfg.DataBits,
|
||||
Parity: serial.Parity(cfg.ParityBit),
|
||||
}
|
||||
var err error
|
||||
serialPort, err = serial.Open(config.portName, mode)
|
||||
serialPort, err = serial.Open(cfg.PortName, mode)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user