235 lines
7.6 KiB
Go
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
|
|
}
|