package sensors import ( "encoding/json" "log/slog" "math/rand" "nats-app/internal/broker" "sync" "time" ) type Simulator struct { *broker.NATS stopChannels map[string]chan bool mu sync.Mutex } func Start(nats *broker.NATS) *Simulator { return &Simulator{ NATS: nats, stopChannels: make(map[string]chan bool), } } func (s *Simulator) SimulateSensor(sensor Sensor) { s.mu.Lock() stopChan := make(chan bool) s.stopChannels[sensor.SensorID] = stopChan s.mu.Unlock() ticker := time.NewTicker(*sensor.SamplingInterval * time.Second) defer ticker.Stop() for { select { case <-stopChan: slog.Info("stopping simulator for sensor", "sensor_id", sensor.SensorID) return case <-ticker.C: data := s.generateData(sensor) if data.SensorID == "" { slog.Warn("sensor data generation failed", "sensor_id", sensor.SensorID) continue } payload, err := json.Marshal(data) if err != nil { slog.Error("failed to marshal sensor data", "error", err, "sensor_id", sensor.SensorID) continue } subject := subjectSensorsData + sensor.SensorID if err := s.Publish(subject, payload); err != nil { slog.Error("failed to publish sensor data", "error", err, "subject", subject) } else { slog.Debug("sensor data published", "sensor_id", sensor.SensorID, "value", data.Value) } } } } func (s *Simulator) UpdateSensor(sensor Sensor) { s.mu.Lock() stopChan, exists := s.stopChannels[sensor.SensorID] s.mu.Unlock() if exists { stopChan <- true s.mu.Lock() delete(s.stopChannels, sensor.SensorID) s.mu.Unlock() } go s.SimulateSensor(sensor) slog.Info("simulator updated for sensor", "sensor_id", sensor.SensorID, "new_interval", sensor.SamplingInterval) } func (s *Simulator) generateData(sensor Sensor) SensorData { now := time.Now() data := SensorData{ SensorID: sensor.SensorID, Timestamp: &now, } if rand.Float64() < 0.05 { return SensorData{} } var value float64 switch sensor.SensorType { case Temperature: value = -20 + rand.Float64()*100 case Humidity: value = 10 + rand.Float64()*90 case CarbonDioxide: value = 980 + rand.Float64()*60 case Pressure: value = 950 + rand.Float64()*100 case Proximity: value = rand.Float64() * 400 case Light: value = rand.Float64() * 10000 default: value = rand.Float64() * 100 } data.Value = &value return data }