diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 909da32..5c8129a 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -23,6 +23,11 @@ builds: - linux - windows - darwin + ldflags: + - -s -w + hooks: + post: + - upx "{{ .Path }}" archives: - format: tar.gz diff --git a/README.md b/README.md index fa92889..d1335af 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ * [x] TCP数据转发 * [x] 参数交互配置 * [x] Ctrl组合键 -* [ ] 文件接收发送 +* [x] 文件接收发送(trzsz lrzsz都支持) ## 运行示例 @@ -42,3 +42,5 @@ 7. Ctrl组合键发送指令.ctrl `.ctrl c` ![img7.png](image/img7.png) +8. 文件上传演示 `index.html` + ![img8.png](image/img8.png) diff --git a/config.go b/config.go index f220355..385c1bf 100644 --- a/config.go +++ b/config.go @@ -3,6 +3,7 @@ package main import ( "log" "net" + "os" ) type Config struct { @@ -18,6 +19,8 @@ type Config struct { logFilePath string forWard int frameSize int + timesTamp bool + timesFmt string address string } type FoeWardMode int @@ -49,3 +52,13 @@ func setForWardClient() (conn net.Conn) { } return conn } + +func checkLogOpen() { + if config.enableLog { + f, err := os.OpenFile(config.logFilePath, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666) + if err != nil { + log.Fatal(err) + } + outs = append(outs, f) + } +} diff --git a/flag.go b/flag.go index e92837f..9f9bbaf 100644 --- a/flag.go +++ b/flag.go @@ -53,8 +53,10 @@ var ( forWard = Flag{ptrVal{int: &config.forWard}, "f", "forward", Val{int: 0}, "转发模式(0: 无 1:TCP-C 2:UDP-C)"} address = Flag{ptrVal{string: &config.address}, "a", "address", Val{string: "127.0.0.1:12345"}, "转发服务地址"} frameSize = Flag{ptrVal{int: &config.frameSize}, "F", "Frame", Val{int: 16}, "帧大小"} + timesTamp = Flag{ptrVal{bool: &config.timesTamp}, "t", "tamp", Val{bool: false}, "是否启用接收时间戳"} + timesFmt = Flag{ptrVal{string: &config.timesFmt}, "T", "TimeFmt", Val{string: "[06-01-02 15:04:05]"}, "时间戳格式化字段"} parityBit = Flag{ptrVal{int: &config.parityBit}, "v", "verify", Val{int: 0}, "奇偶校验(0:无校验、1:奇校验、2:偶校验、3:1校验、4:0校验)"} - flags = []Flag{portName, baudRate, dataBits, stopBits, outputCode, inputCode, endStr, enableLog, logFilePath, forWard, address, frameSize, parityBit} + flags = []Flag{portName, baudRate, dataBits, stopBits, outputCode, inputCode, endStr, enableLog, logFilePath, forWard, address, frameSize, timesTamp, timesFmt, parityBit} ) var ( @@ -178,6 +180,24 @@ func getCliFlag() { ).Display() if v { config.inputCode = "hex" + b, _ := inf.NewText( + text.WithPrompt("Frames:"), + text.WithPromptStyle(theme.DefaultTheme.PromptStyle), + text.WithDefaultValue("16"), + ).Display() + config.frameSize, _ = strconv.Atoi(b) + } + v, _ = inf.NewConfirmWithSelection( + confirm.WithPrompt("启用时间戳"), + ).Display() + if v { + config.inputCode = "hex" + b, _ := inf.NewText( + text.WithPrompt("Frames:"), + text.WithPromptStyle(theme.DefaultTheme.PromptStyle), + text.WithDefaultValue("16"), + ).Display() + config.frameSize, _ = strconv.Atoi(b) } v, _ = inf.NewConfirmWithSelection( confirm.WithPrompt("启用高级配置"), diff --git a/go.mod b/go.mod index 80c9074..1984dde 100644 --- a/go.mod +++ b/go.mod @@ -3,35 +3,51 @@ module COM go 1.22 require ( + github.com/charmbracelet/bubbles v0.18.0 + github.com/charmbracelet/bubbletea v0.25.0 github.com/fzdwx/infinite v0.12.1 + github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 + github.com/trzsz/trzsz-go v1.1.7 github.com/zimolab/charsetconv v0.1.2 go.bug.st/serial v1.6.2 ) require ( + github.com/UserExistsError/conpty v0.1.2 // indirect + github.com/akavel/rsrc v0.10.2 // indirect + github.com/alexflint/go-scalar v1.2.0 // indirect github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect - github.com/charmbracelet/bubbles v0.16.1 // indirect - github.com/charmbracelet/bubbletea v0.24.2 // indirect - github.com/charmbracelet/lipgloss v0.7.1 // indirect + github.com/charmbracelet/lipgloss v0.9.1 // indirect + github.com/chzyer/readline v1.5.1 // indirect github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect github.com/creack/goselect v0.1.2 // indirect + github.com/creack/pty v1.1.21 // indirect + github.com/dchest/jsmin v0.0.0-20220218165748-59f39799265f // indirect github.com/duke-git/lancet/v2 v2.2.1 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/fzdwx/iter v0.0.0-20230511075109-0afee9319312 // indirect + github.com/josephspurrier/goversioninfo v1.4.0 // indirect + github.com/klauspost/compress v1.17.4 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-localereader v0.0.1 // indirect - github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/reflow v0.3.0 // indirect - github.com/muesli/termenv v0.15.1 // indirect - github.com/rivo/uniseg v0.4.4 // indirect + github.com/muesli/termenv v0.15.2 // indirect + github.com/ncruces/zenity v0.10.10 // indirect + github.com/randall77/makefat v0.0.0-20210315173500-7ddd0e42c844 // indirect + github.com/rivo/uniseg v0.4.6 // indirect github.com/rotisserie/eris v0.5.4 // indirect - github.com/sahilm/fuzzy v0.1.0 // indirect + github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f // indirect + github.com/trzsz/go-arg v1.5.3 // indirect + github.com/trzsz/promptui v0.10.5 // indirect golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect + golang.org/x/image v0.14.0 // indirect golang.org/x/sync v0.2.0 // indirect golang.org/x/sys v0.19.0 // indirect golang.org/x/term v0.19.0 // indirect - golang.org/x/text v0.9.0 // indirect + golang.org/x/text v0.14.0 // indirect ) diff --git a/image/img8.png b/image/img8.png new file mode 100644 index 0000000..7fdabff Binary files /dev/null and b/image/img8.png differ diff --git a/main.go b/main.go index 51f12ae..7dfe378 100644 --- a/main.go +++ b/main.go @@ -4,13 +4,18 @@ import ( "bufio" "flag" "fmt" + "github.com/trzsz/trzsz-go/trzsz" "github.com/zimolab/charsetconv" "go.bug.st/serial" + "golang.org/x/term" "io" "log" "net" "os" + "os/signal" + "runtime" "strings" + "time" ) var ( @@ -20,10 +25,15 @@ var ( ) var ( - in io.Reader = os.Stdin - out io.Writer = os.Stdout - ins = []io.Reader{os.Stdin} - outs = []io.Writer{os.Stdout} + in io.Reader = os.Stdin + out io.Writer = os.Stdout + ins = []io.Reader{os.Stdin} + outs = []io.Writer{os.Stdout} + trzszFilter *trzsz.TrzszFilter + clientIn *io.PipeReader + stdoutPipe *io.PipeReader + stdinPipe *io.PipeWriter + clientOut *io.PipeWriter ) func checkPortAvailability(name string) ([]string, error) { @@ -50,15 +60,6 @@ func init() { for _, f := range flags { flagInit(&f) } - flag.Func("h", "获取帮助", func(s string) error { - ports, err := checkPortAvailability(s) - if err != nil { - fmt.Println(err) - printUsage(ports) - os.Exit(0) - } - return err - }) cmdinit() } @@ -76,11 +77,11 @@ func input(in io.Reader) { } } if !ok { - _, err := io.WriteString(serialPort, input.Text()) + _, err := io.WriteString(stdinPipe, input.Text()) if err != nil { log.Fatal(err) } - _, err = io.WriteString(serialPort, config.endStr) + _, err = io.WriteString(stdinPipe, config.endStr) if err != nil { log.Fatal(err) } @@ -101,13 +102,24 @@ func strout(out io.Writer, cs, str string) { func output() { if strings.Compare(config.inputCode, "hex") == 0 { - b := make([]byte, 16) - r, _ := io.LimitReader(serialPort, int64(config.frameSize)).Read(b) + b := make([]byte, config.frameSize) + r, _ := io.LimitReader(stdoutPipe, int64(config.frameSize)).Read(b) if r != 0 { - strout(out, config.outputCode, fmt.Sprintf("% X %q \n", b, b)) + if config.timesTamp { + strout(out, config.outputCode, fmt.Sprintf("%v % X %q \n", time.Now().Format(config.timesFmt), b, b)) + } else { + strout(out, config.outputCode, fmt.Sprintf("% X %q \n", b, b)) + } } } else { - err = charsetconv.ConvertWith(serialPort, charsetconv.Charset(config.inputCode), out, charsetconv.Charset(config.outputCode), false) + if config.timesTamp { + line, _, _ := bufio.NewReader(stdoutPipe).ReadLine() + if line != nil { + strout(out, config.outputCode, fmt.Sprintf("%v %s\n", time.Now().Format(config.timesFmt), line)) + } + } else { + err = charsetconv.ConvertWith(stdoutPipe, charsetconv.Charset(config.inputCode), out, charsetconv.Charset(config.outputCode), false) + } } if err != nil { log.Fatal(err) @@ -115,7 +127,6 @@ func output() { } func main() { flag.Parse() - if config.portName == "" { getCliFlag() } @@ -141,6 +152,33 @@ func main() { log.Fatal(err) } }(serialPort) + fd := int(os.Stdin.Fd()) + width, _, err := term.GetSize(fd) + if err != nil { + if runtime.GOOS != "windows" { + fmt.Printf("term get size failed: %s\n", err) + return + } + width = 80 + } + + clientIn, stdinPipe = io.Pipe() + stdoutPipe, clientOut = io.Pipe() + trzszFilter = trzsz.NewTrzszFilter(clientIn, clientOut, serialPort, serialPort, + trzsz.TrzszOptions{TerminalColumns: int32(width), EnableZmodem: true}) + trzsz.SetAffectedByWindows(false) + ch := make(chan os.Signal, 1) + go func() { + for range ch { + width, _, err := term.GetSize(fd) + if err != nil { + fmt.Printf("term get size failed: %s\n", err) + continue + } + trzszFilter.SetTerminalColumns(int32(width)) + } + }() + defer func() { signal.Stop(ch); close(ch) }() if FoeWardMode(config.forWard) != NOT { conn := setForWardClient() @@ -153,18 +191,13 @@ func main() { } }(conn) } + checkLogOpen() + if len(ins) != 0 { for _, reader := range ins { go input(reader) } } - if config.enableLog { - f, err := os.OpenFile(config.logFilePath, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666) - if err != nil { - log.Fatal(err) - } - outs = append(outs, f) - } if len(outs) != 1 { out = io.MultiWriter(outs...) }