update emulator

This commit is contained in:
2026-02-19 11:43:40 +01:00
parent 27517bd2ea
commit 5e448a14e2
7 changed files with 109 additions and 22 deletions

View File

@@ -5,6 +5,7 @@ import (
"log"
"math/rand"
"os"
"sync"
"time"
"ble_simulator/internal/ble"
@@ -30,15 +31,30 @@ func main() {
// Channel for alarm notifications from TUI
notifyCh := make(chan string, 10)
// Channel for disconnect-all-clients signal
disconnectCh := make(chan struct{}, 1)
// Connection registry for disconnect-on-tamper
connRegistry := make(map[string]bluetooth.Device)
var connMu sync.Mutex
// Connection handler
adapter.SetConnectHandler(func(dev bluetooth.Device, connected bool) {
connMu.Lock()
if connected {
connRegistry[dev.Address.String()] = dev
state.IncrConnectedClients()
logBuffer.Conn("Client Connected: %s", dev.Address.String())
} else {
delete(connRegistry, dev.Address.String())
state.DecrConnectedClients()
logBuffer.Conn("Client Disconnected")
// Auto-disable prog mode when no clients connected
if state.GetConnectedClients() == 0 {
state.SetProgMode(false)
}
}
connMu.Unlock()
})
// Setup BLE service with logger
@@ -67,11 +83,26 @@ func main() {
logBuffer.Info("Notify Char: 6e400003-b5a3-f393-e0a9-e50e24dcca9e (Notify/TX)")
logBuffer.Info("Waiting for connections...")
// Start disconnect handler goroutine
go func() {
for range disconnectCh {
connMu.Lock()
for addr, dev := range connRegistry {
if err := dev.Disconnect(); err != nil {
logBuffer.Err("Failed to disconnect %s: %v", addr, err)
} else {
logBuffer.Info("Disconnected client: %s", addr)
}
}
connMu.Unlock()
}
}()
// Start notification loop (500ms heartbeat with sensor data)
go notificationLoop(notifyChar, state, logBuffer, notifyCh)
// Create and run TUI
model := tui.NewModel(state, logBuffer, notifyCh)
model := tui.NewModel(state, logBuffer, notifyCh, disconnectCh)
p := tea.NewProgram(model, tea.WithAltScreen())
if _, err := p.Run(); err != nil {
@@ -88,31 +119,33 @@ func notificationLoop(char *bluetooth.Characteristic, state *device.DeviceState,
// Check for alarm state and send alarm message if active
if alarmActive, alarmType := state.GetAlarmState(); alarmActive {
msg := fmt.Sprintf("ALARM: %s", alarmType)
_, err := char.Write([]byte(msg + "\n"))
_, err := char.Write([]byte(msg + "\r\n"))
if err == nil {
logger.TX("Sending: %s", msg)
}
}
// Send regular sensor data
value := state.GetSensorValue()
jitter := state.GetJitterRange()
jitterVal := 0
if jitter > 0 {
jitterVal = rand.Intn(jitter*2+1) - jitter
}
msg := fmt.Sprintf("SENSOR:%d", value+jitterVal)
// Only send sensor data in programming mode
if state.GetProgMode() {
value := state.GetSensorValue()
jitter := state.GetJitterRange()
jitterVal := 0
if jitter > 0 {
jitterVal = rand.Intn(jitter*2+1) - jitter
}
msg := fmt.Sprintf("SENSOR:%d", value+jitterVal)
_, err := char.Write([]byte(msg + "\n"))
if err != nil {
// Silently ignore write errors (no subscribers)
continue
_, err := char.Write([]byte(msg + "\r\n"))
if err != nil {
// Silently ignore write errors (no subscribers)
continue
}
logger.TX("Sending: %s", msg)
}
logger.TX("Sending: %s", msg)
case alarmMsg := <-notifyCh:
// Handle alarm notifications from TUI
_, err := char.Write([]byte(alarmMsg + "\n"))
_, err := char.Write([]byte(alarmMsg + "\r\n"))
if err == nil {
logger.TX("Sending: %s", alarmMsg)
}