Files
fire_leave_tool/gpio_monitor/gpio_monitor.go
T
2026-05-12 15:45:50 +08:00

235 lines
7.6 KiB
Go

package gpio_monitor
import (
"context"
"fmt"
"os"
"strconv"
"time"
"FireLeave_tool/logger"
"FireLeave_tool/reporter"
)
const (
// ButtonGPIO GPIO引脚编号
ButtonGPIO = 111
// DebounceTime 去抖动时间
DebounceTime = 200 * time.Millisecond
)
// GPIOButtonMonitor GPIO按钮监视器
type GPIOButtonMonitor struct {
gpioPin int
isRunning bool
lastState bool
lastChange time.Time
lastAlertTime time.Time
cooldownTime time.Duration
cooldownActive bool
}
// NewGPIOButtonMonitor 创建GPIO按钮监视器
func NewGPIOButtonMonitor(gpioPin int) *GPIOButtonMonitor {
return &GPIOButtonMonitor{
gpioPin: gpioPin,
isRunning: false,
lastState: false,
lastChange: time.Now(),
lastAlertTime: time.Now().Add(-6 * time.Second), // 初始化为6秒前,确保第一次可以触发
cooldownTime: 5 * time.Second, // 5秒冷却时间
cooldownActive: false, // 冷却期状态
}
}
// InitGPIO 初始化GPIO
func (m *GPIOButtonMonitor) InitGPIO() error {
gpioPath := fmt.Sprintf("/sys/class/gpio/gpio%d", m.gpioPin)
// 检查GPIO是否已导出
if _, err := os.Stat(gpioPath); os.IsNotExist(err) {
// 导出GPIO
if err := os.WriteFile("/sys/class/gpio/export", []byte(strconv.Itoa(m.gpioPin)), 0666); err != nil {
return fmt.Errorf("failed to export GPIO %d: %v", m.gpioPin, err)
}
// 等待导出完成
time.Sleep(50 * time.Millisecond)
}
// 检查并设置方向为输入
directionPath := fmt.Sprintf("%s/direction", gpioPath)
directionBytes, err := os.ReadFile(directionPath)
if err != nil {
return fmt.Errorf("failed to read GPIO direction: %v", err)
}
direction := string(directionBytes)
if direction != "in\n" && direction != "in" {
if err := os.WriteFile(directionPath, []byte("in"), 0666); err != nil {
return fmt.Errorf("failed to set GPIO direction to in: %v", err)
}
// 等待设置完成
time.Sleep(10 * time.Millisecond)
}
if log := logger.GlobalLoggerManager.GetLogger("gpio"); log != nil {
log.Printf("GPIO %d initialized successfully as input", m.gpioPin)
} else if log := logger.GlobalLoggerManager.GetLogger("reporter"); log != nil {
log.Printf("GPIO %d initialized successfully as input", m.gpioPin)
}
return nil
}
// ReadGPIOValue 读取GPIO值
func (m *GPIOButtonMonitor) ReadGPIOValue() (bool, error) {
valuePath := fmt.Sprintf("/sys/class/gpio/gpio%d/value", m.gpioPin)
valueBytes, err := os.ReadFile(valuePath)
if err != nil {
return false, fmt.Errorf("failed to read GPIO value: %v", err)
}
value := string(valueBytes)
return value == "1\n" || value == "1", nil
}
// Start 启动GPIO监控
func (m *GPIOButtonMonitor) Start(ctx context.Context) error {
// 初始化GPIO
if err := m.InitGPIO(); err != nil {
return fmt.Errorf("failed to initialize GPIO: %v", err)
}
m.isRunning = true
if log := logger.GlobalLoggerManager.GetLogger("gpio"); log != nil {
log.Printf("Started monitoring GPIO %d for button alerts", m.gpioPin)
} else if log := logger.GlobalLoggerManager.GetLogger("reporter"); log != nil {
log.Printf("Started monitoring GPIO %d for button alerts", m.gpioPin)
}
go func() {
defer func() {
m.isRunning = false
if log := logger.GlobalLoggerManager.GetLogger("gpio"); log != nil {
log.Printf("Stopped monitoring GPIO %d", m.gpioPin)
} else if log := logger.GlobalLoggerManager.GetLogger("reporter"); log != nil {
log.Printf("Stopped monitoring GPIO %d", m.gpioPin)
}
}()
for {
select {
case <-ctx.Done():
return
default:
// 读取GPIO值
currentState, err := m.ReadGPIOValue()
if err != nil {
if log := logger.GlobalLoggerManager.GetLogger("gpio"); log != nil {
log.Printf("Error reading GPIO %d value: %v", m.gpioPin, err)
} else if log := logger.GlobalLoggerManager.GetLogger("reporter"); log != nil {
log.Printf("Error reading GPIO %d value: %v", m.gpioPin, err)
}
time.Sleep(100 * time.Millisecond)
continue
}
// 检查状态是否变化
if currentState != m.lastState {
// 记录状态变化
if log := logger.GlobalLoggerManager.GetLogger("gpio"); log != nil {
log.Printf("GPIO %d state changed: %t → %t", m.gpioPin, m.lastState, currentState)
} else if log := logger.GlobalLoggerManager.GetLogger("reporter"); log != nil {
log.Printf("GPIO %d state changed: %t → %t", m.gpioPin, m.lastState, currentState)
}
// 状态变化时更新lastChange
m.lastChange = time.Now()
// 立即更新lastState,避免重复触发状态变化日志
m.lastState = currentState
// 只输出true状态
if currentState {
if log := logger.GlobalLoggerManager.GetLogger("gpio"); log != nil {
log.Printf("GPIO %d current state: %t", m.gpioPin, currentState)
} else if log := logger.GlobalLoggerManager.GetLogger("reporter"); log != nil {
log.Printf("GPIO %d current state: %t", m.gpioPin, currentState)
}
}
}
// 检查冷却期状态
if time.Since(m.lastAlertTime) < m.cooldownTime {
if !m.cooldownActive {
m.cooldownActive = true
// 只记录一次冷却期开始
}
} else {
if m.cooldownActive {
// 只记录一次冷却期结束
if log := logger.GlobalLoggerManager.GetLogger("gpio"); log != nil {
log.Printf("GPIO %d cooldown period ended", m.gpioPin)
} else if log := logger.GlobalLoggerManager.GetLogger("reporter"); log != nil {
log.Printf("GPIO %d cooldown period ended", m.gpioPin)
}
m.cooldownActive = false
}
}
// 检查是否稳定,且当前状态为true(高电平)
if currentState && time.Since(m.lastChange) > DebounceTime {
// 检查冷却期
if time.Since(m.lastAlertTime) >= m.cooldownTime {
// 检测到稳定的高电平,切换事件周期
if log := logger.GlobalLoggerManager.GetLogger("gpio"); log != nil {
log.Printf("GPIO %d detected stable high level after debounce, switching event cycle...", m.gpioPin)
} else if log := logger.GlobalLoggerManager.GetLogger("reporter"); log != nil {
log.Printf("GPIO %d detected stable high level after debounce, switching event cycle...", m.gpioPin)
}
// 切换事件周期
var targetCycle reporter.EventCycle
switch reporter.GetCurrentEventCycle() {
case reporter.EventCycleFireLeave:
targetCycle = reporter.EventCycleFireCheck
case reporter.EventCycleFireCheck:
targetCycle = reporter.EventCycleFireLeave
default:
targetCycle = reporter.EventCycleFireCheck
}
if err := reporter.SwitchEventCycle(targetCycle); err != nil {
if log := logger.GlobalLoggerManager.GetLogger("gpio"); log != nil {
log.Printf("Failed to switch event cycle: %v", err)
} else if log := logger.GlobalLoggerManager.GetLogger("reporter"); log != nil {
log.Printf("Failed to switch event cycle: %v", err)
}
}
// 更新lastAlertTime,开始冷却期
m.lastAlertTime = time.Now()
if log := logger.GlobalLoggerManager.GetLogger("gpio"); log != nil {
log.Printf("GPIO %d alert sent, cooldown period started (5s)", m.gpioPin)
} else if log := logger.GlobalLoggerManager.GetLogger("reporter"); log != nil {
log.Printf("GPIO %d alert sent, cooldown period started (5s)", m.gpioPin)
}
// 重置lastChange,避免重复触发
m.lastChange = time.Now()
}
}
// 增加休眠时间,减少CPU占用和日志频率
time.Sleep(100 * time.Millisecond)
}
}
}()
return nil
}
// IsRunning 检查监视器是否正在运行
func (m *GPIOButtonMonitor) IsRunning() bool {
return m.isRunning
}