first commit

This commit is contained in:
zhaohengze
2026-05-14 17:53:52 +08:00
commit e1ffcf2e7d
156 changed files with 109026 additions and 0 deletions

164
poweroff_linux/shutdown.go Normal file
View File

@ -0,0 +1,164 @@
// 通过串口接收指令触发系统关机的程序
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 失败: %v5秒后重试...", 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)
}
}