// 通过串口接收指令触发系统关机的程序 package main import ( "bytes" "encoding/json" "log" "os" "os/exec" "strings" "time" "go.bug.st/serial" ) // Config 配置结构体,从 config.json 文件读取 type Config struct { SerialPort string `json:"port"` // 串口设备路径,如 COM1 或 /dev/ttyUSB0 BaudRate int `json:"baudRate"` // 串口波特率 ShutdownWord string `json:"shutdownWord"` // 触发关机的指令文本 } // loadConfig 从指定路径加载配置文件并解析为 Config 结构体 func loadConfig(configPath string) (*Config, error) { data, err := os.ReadFile(configPath) if err != nil { return nil, err } var config Config err = json.Unmarshal(data, &config) if err != nil { return nil, err } return &config, nil } func main() { // 加载配置文件 config, err := loadConfig("config.json") if err != nil { log.Fatalf("加载配置文件失败: %v", err) } log.Printf("已监听串口 %s (波特率 %d),等待指令 '%s'...", config.SerialPort, config.BaudRate, config.ShutdownWord) // 串口读取缓冲区和数据接收缓冲区 buf := make([]byte, 1024) var buffer bytes.Buffer // 外层循环:持续尝试连接串口 for { mode := &serial.Mode{ BaudRate: config.BaudRate, } // 尝试打开串口 port, err := serial.Open(config.SerialPort, mode) if err != nil { log.Printf("打开串口 %s 失败: %v,5秒后重试...", config.SerialPort, err) time.Sleep(5 * time.Second) continue } defer port.Close() log.Printf("串口 %s 已连接", config.SerialPort) // 设置读取超时为1秒,避免阻塞 port.SetReadTimeout(time.Second * 1) // 串口初始化后发送 start 指令 _, err = port.Write([]byte("start\r\n")) if err != nil { log.Printf("发送 start 失败: %v", err) port.Close() continue } log.Printf("已发送 start") // 启动定时发送 blive的协程 sendDone := make(chan struct{}) go func() { ticker := time.NewTicker(time.Second) defer ticker.Stop() for { select { case <-ticker.C: _, err := port.Write([]byte("blive\r\n")) if err != nil { log.Printf("发送 blive 失败: %v", err) return } case <-sendDone: return } } }() // 内层循环:读取串口数据 for { n, err := port.Read(buf) if err != nil { if err.Error() == "timeout" { continue } log.Printf("读取串口错误: %v,重新连接...", err) close(sendDone) port.Close() break } if n > 0 { // 将接收到的数据写入缓冲区 buffer.Write(buf[:n]) data := buffer.String() // 检查是否接收到完整的一行数据(以 \r\n 或 \n 结尾) if strings.Contains(data, "\r\n") || strings.Contains(data, "\n") { var lines []string if strings.Contains(data, "\r\n") { lines = strings.Split(dataAfter(data, "\r\n"), "\r\n") } else { lines = strings.Split(dataAfter(data, "\n"), "\n") } // 处理每一行数据 for _, line := range lines { line = strings.TrimSpace(line) if line == "" { continue } log.Printf("收到: %s", line) // 如果匹配到关机指令,执行关机 if line == config.ShutdownWord { executeShutdown() } } buffer.Reset() } } } } } // dataAfter 获取字符串中最后一个分隔符之前的内容 // 用于处理跨数据包的行边界问题,保留最后一个不完整的行 func dataAfter(s, sep string) string { idx := strings.LastIndex(s, sep) if idx == -1 { return s } return s[:idx] } // executeShutdown 执行系统关机命令 func executeShutdown() { log.Println("触发关机指令!") log.Println("执行关机命令: shutdown -h now") cmd := exec.Command("shutdown", "-h", "now") err := cmd.Run() if err != nil { log.Fatalf("关机命令执行失败: %v", err) } }