mirror of
https://github.com/IS1DI/x0gp.git
synced 2026-07-16 20:47:55 +00:00
feat(server): integrate UDP video service and normalize database schema
- Implement UDPVideoService to stream MJPEG video frames from ESP32s via UDP and send control commands. - Normalize race database schema: - Remove redundant driver_ids array from races table (migration 011). - Move per-driver outcomes (total_time_ms, best_lap_ms, position) to race_drivers table (migration 012). - Add device_id column to lobby_drivers to link active physical devices (migration 013). - Update WebSocket handler to pass lobbySvc, driversSvc, and videoSvc for driver identity tracking, syncing in-memory driver metadata, and forwarding WS controls to physical cars. - Add CORS middleware to HTTP routes. - Regenerate Swagger documentation.
This commit is contained in:
@@ -94,7 +94,9 @@ func (s *Service) CreateRace(opts CreateRaceOptions) (RaceMeta, error) {
|
||||
hook(meta)
|
||||
}
|
||||
s.bump("race_created", id, "")
|
||||
s.mirror(func(ctx context.Context, p Persistence) error { return p.UpsertRace(ctx, meta) })
|
||||
// Synchronous mirror so the race row is in Postgres before any
|
||||
// subsequent AddDriver mirrors try to attach drivers (FK).
|
||||
s.mirrorSync(func(ctx context.Context, p Persistence) error { return p.UpsertRace(ctx, meta) })
|
||||
return meta, nil
|
||||
}
|
||||
|
||||
@@ -185,6 +187,7 @@ func (s *Service) RemoveDriver(id string) {
|
||||
d.Status = DriverStatusOffline
|
||||
d.LastSeenMs = time.Now().UnixMilli()
|
||||
d.RaceID = ""
|
||||
d.DeviceID = nil
|
||||
hook := s.onRaceRemoved
|
||||
s.mu.Unlock()
|
||||
|
||||
@@ -241,6 +244,10 @@ func (s *Service) AddDriverToRace(driverID, raceID string) error {
|
||||
s.mu.Unlock()
|
||||
return ErrDriverNotFound
|
||||
}
|
||||
if d.DeviceID == nil {
|
||||
s.mu.Unlock()
|
||||
return fmt.Errorf("driver %s has no device selected", driverID)
|
||||
}
|
||||
r, ok := s.races[raceID]
|
||||
if !ok {
|
||||
s.mu.Unlock()
|
||||
@@ -299,6 +306,7 @@ func (s *Service) RemoveDriverFromRace(driverID string) {
|
||||
}
|
||||
d.Status = DriverStatusIdle
|
||||
d.RaceID = ""
|
||||
d.DeviceID = nil
|
||||
prevRaceID := d.RaceID
|
||||
hook := s.onRaceRemoved
|
||||
s.mu.Unlock()
|
||||
@@ -341,6 +349,7 @@ func (s *Service) SetRaceStatus(raceID string, status RaceStatus) error {
|
||||
if d, ok := s.drivers[did]; ok {
|
||||
d.Status = DriverStatusIdle
|
||||
d.RaceID = ""
|
||||
d.DeviceID = nil
|
||||
affected = append(affected, *d)
|
||||
}
|
||||
}
|
||||
@@ -445,6 +454,44 @@ func (s *Service) GetDriver(driverID string) (DriverMeta, error) {
|
||||
return *d, nil
|
||||
}
|
||||
|
||||
// SelectDevice assigns a physical device ID (0..127) to a driver.
|
||||
// Pass deviceID = nil to release the device.
|
||||
// Returns an error if the device is already taken by another online driver.
|
||||
func (s *Service) SelectDevice(driverID string, deviceID *int) error {
|
||||
if deviceID != nil {
|
||||
if *deviceID < 0 || *deviceID > 127 {
|
||||
return fmt.Errorf("invalid device ID: must be 0..127")
|
||||
}
|
||||
}
|
||||
|
||||
s.mu.Lock()
|
||||
d, ok := s.drivers[driverID]
|
||||
if !ok {
|
||||
s.mu.Unlock()
|
||||
return ErrDriverNotFound
|
||||
}
|
||||
|
||||
// Enforce that device cannot be selected if already assigned to another driver.
|
||||
if deviceID != nil {
|
||||
for otherID, otherD := range s.drivers {
|
||||
if otherID != driverID && otherD.Status != DriverStatusOffline && otherD.DeviceID != nil && *otherD.DeviceID == *deviceID {
|
||||
s.mu.Unlock()
|
||||
return fmt.Errorf("device %d is already taken by driver %s", *deviceID, otherID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
d.DeviceID = deviceID
|
||||
out := *d
|
||||
s.mu.Unlock()
|
||||
|
||||
s.bump("driver_updated", "", driverID)
|
||||
s.mirror(func(ctx context.Context, p Persistence) error {
|
||||
return p.UpsertDriver(ctx, out)
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListRaces returns a snapshot copy of all races.
|
||||
func (s *Service) ListRaces() []RaceMeta {
|
||||
s.mu.RLock()
|
||||
|
||||
Reference in New Issue
Block a user