package main import ( "bufio" "context" "errors" "fmt" "io" "os" "os/signal" "strings" "sync/atomic" "syscall" "time" "github.com/google/uuid" "hnyfkj.com.cn/rtu/linux/baseapp" ) const MODULE_NAME = "YFKJ_SSH_CLIENT" var ( coupler *MQTTCoupler Version = "1.0.0.1" ErrBrokerAddressEmpty = errors.New("mqtt server address is empty") ErrIMEINotAvailable = errors.New("device imei is not available") ) func main() { if baseapp.IsArgsParam("-h") { help() return } if baseapp.IsArgsParam("-v") { fmt.Println("程序版本:", Version, "\n构建时间:", baseapp.BuildTime) return } devIMEI := baseapp.GetArgsParamStr("-c", "") if devIMEI == "" { help() return } if err := loadAppConfig(); err != nil { fmt.Printf("[%s] 错误: %v!!\n", MODULE_NAME, err) return } if CfgServers.MQTTSrv.Address == "" { fmt.Printf("[%s] 错误: %v!!\n", MODULE_NAME, ErrBrokerAddressEmpty) return } ctx, cancel := context.WithCancel(context.Background()) coupler = &MQTTCoupler{ ctx: ctx, cancel: cancel, broker: CfgServers.MQTTSrv.Address, username: CfgServers.MQTTSrv.Username, password: CfgServers.MQTTSrv.Password, clientID: uuid.New().String(), imei: devIMEI, cwd: "/", } if err := coupler.init2(); err != nil { fmt.Printf("[%s] 错误: %v\n!!", MODULE_NAME, err) return } term() // 启动终端模拟器卍 } // SHELL终端模拟器 // 1, 连接远程设备, 是否成功 // 2, 等待用户输入, 封装请求 // 3, 没有用户输入, 封装心跳 // 4, 发送请求数据, 远程执行 // 5, 耗时用户请求, 允许中断, Ctrl+C // 6, 等待返回结果, 打印输出 // 7, 循环等待下次, 直到退出 func term() { var pingState atomic.Bool var executing atomic.Bool // -在线-心跳检测 heartbeatLoop(&pingState) // Ctrl+C中断处理 interruptLoop(&executing) reader := bufio.NewReader(os.Stdin) for { if !pingState.Load() { fmt.Printf("[%s] 无法连接目标设备!!\n", MODULE_NAME) break } fmt.Print("\033[?25h") // 显示光标 fmt.Printf("root@%s:%s# ", coupler.imei, coupler.cwd) input, err := reader.ReadString('\n') if err != nil { if err == io.EOF { os.Exit(0) } fmt.Println("读取用户输入失败:", err) continue } input = strings.TrimSpace(input) if input == "" { continue } switch input { case "quit": _, _ = coupler.quit() os.Exit(0) } fmt.Print("\033[?25l") // 隐藏光标 executing.Store(true) result, err := coupler.exec(input) executing.Store(false) if err != nil { fmt.Printf("执行错误: %v\n", err) continue } if result.Stdout != "" { fmt.Println(result.Stdout) } if result.Stderr != "" { fmt.Fprintln(os.Stderr, result.Stderr) } if result.Cwd != "" { coupler.cwd = result.Cwd } } } func help() { h := ` -h 显示帮助提示 -v 当前程序版本 -c 连接目标设备(IMEI), 例如: -c 869523059113051 ` fmt.Println(h) } func interruptLoop(executing *atomic.Bool) { sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, syscall.SIGINT) go func() { for range sigCh { if executing.Load() { _, _ = coupler.stop() } fmt.Print("\n^C\n") } // end for }() } func heartbeatLoop(pingState *atomic.Bool) { go func() { ticker := time.NewTicker(1 * time.Second) defer ticker.Stop() for range ticker.C { resp, err := coupler.ping() if err == nil && resp.Error == nil && resp.Result != nil && string(resp.Result) == "pong" { pingState.Store(true) } else { pingState.Store(false) } // end if } // end for }() }