diff --git a/components/paddock/telemetry/pitcrew/coach.py b/components/paddock/telemetry/pitcrew/coach.py index 714dbcb8..82587327 100755 --- a/components/paddock/telemetry/pitcrew/coach.py +++ b/components/paddock/telemetry/pitcrew/coach.py @@ -20,7 +20,6 @@ class Coach(LoggingMixin): def __init__(self, history: History, db_coach: DbCoach, debug=False): self.history = history - self.previous_history_error = None self.db_coach = db_coach self.messages = [] self.previous_distance = 10_000_000 @@ -30,6 +29,9 @@ def __init__(self, history: History, db_coach: DbCoach, debug=False): self.session_id = "NO_SESSION" self.track_walk = False self.distance = 0 + self._new_session_starting = False + self._next_messages = [] + self._error = None def filter_from_topic(self, topic): frags = topic.split("/") @@ -47,47 +49,79 @@ def filter_from_topic(self, topic): } return filter - def set_filter(self, filter): + def new_session(self, topic): + self._new_session_starting = True + self.topic = topic + self.log_debug("new session %s", topic) + filter = self.filter_from_topic(topic) self.history.set_filter(filter) self.session_id = filter.get("SessionId", "NO_SESSION") self.messages = [] + self._next_messages = [] + self.responses = {} + self.db_coach.refresh_from_db() + self.track_walk = self.db_coach.track_walk + + def ready(self): + if self._new_session_starting: + if not self.history.is_ready(): + if self.history.is_initializing(): + return False + error = self.history.get_and_reset_error() + if error: + self.db_coach.error = error + self.db_coach.save() + self._error = error + return False + + self.init_messages() + + startup_message = "start coaching " + try: + lap_time = self.history.fast_lap.laps.first().time_human() + startup_message += f"for a lap time of {lap_time}" + except Exception: + pass + + if self.track_walk: + startup_message += " doing a track walk" + + self.say_next(startup_message) + self.db_coach.status = startup_message + self.db_coach.fast_lap = self.history.fast_lap + self.db_coach.error = "" + self.db_coach.save() + + self.track_length = self.history.track_length + + self._new_session_starting = False + + return True + + def get_and_reset_error(self): + if self._error: + error = self._error + self._error = None + return error + + def say_next(self, msg): + self._next_messages.append(msg) def notify(self, topic, telemetry, now=None): now = now or django.utils.timezone.now() if self.topic != topic: - self.topic = topic - self.log_debug("new session %s", topic) - self.set_filter(self.filter_from_topic(topic)) - self.startup_message = "" - - if not self.history.ready: - if self.history.error: - self.db_coach.error = self.history.error - self.db_coach.save() - # clear history error - error_msg = self.history.error - self.history.error = None - return (self.response_topic, error_msg) + self.new_session(topic) + + if not self.ready(): + error = self.get_and_reset_error() + if error: + return (self.response_topic, error) return None - if self.history.ready and self.history.startup_message: - self.track_length = self.history.track.length - if self.startup_message != self.history.startup_message: - self.startup_message = self.history.startup_message - self.history.startup_message = "" - self.db_coach.refresh_from_db() - self.track_walk = self.db_coach.track_walk - if self.track_walk: - self.startup_message += " doing a track walk" - self.db_coach.status = self.startup_message - self.db_coach.fast_lap = self.history.fast_lap - self.db_coach.error = "" - self.db_coach.save() - return (self.response_topic, self.startup_message) - - if not self.messages: - self.init_messages() - self.responses = {} + if self._next_messages: + messages = self._next_messages + self._next_messages = [] + return (self.response_topic, messages) self.distance = int(telemetry["DistanceRoundTrack"]) if self.distance == self.previous_distance: @@ -135,7 +169,7 @@ def collect_responses(self, distance, telemetry): # FIXME: +100 should be speed dependent # - future_distance = (distance + 20) % self.history.track_length + future_distance = (distance + 20) % self.track_length responses = self.get_responses(telemetry, future_distance) for response in responses: distance = response["distance"] @@ -150,7 +184,7 @@ def collect_responses(self, distance, telemetry): # so we send the message less than 10 seconds before its due # if we send messages too late, the driver will have passed the distance # find the distance where - future_distance = (distance + 10) % self.history.track_length + future_distance = (distance + 10) % self.track_length responses = self.responses.pop(future_distance, None) if responses: if len(responses) > 1: diff --git a/components/paddock/telemetry/pitcrew/history.py b/components/paddock/telemetry/pitcrew/history.py index 8f05c8ff..55256288 100755 --- a/components/paddock/telemetry/pitcrew/history.py +++ b/components/paddock/telemetry/pitcrew/history.py @@ -10,12 +10,11 @@ class History(LoggingMixin): def __init__(self): - self.pickle = False - self.do_init = False + self._do_init = False self.segments = [] self.previous_update_meters = 0 - self.ready = False - self.error = None + self._ready = False + self._error = None self.do_run = True self.driver = None self.track_length = 0 @@ -25,7 +24,6 @@ def __init__(self): self.fast_lap = None self.process_segments = [] self.threaded = False - self.startup_message = "" self.session_id = "NO_SESSION" def disconnect(self): @@ -35,22 +33,27 @@ def run(self): self.threaded = True while self.do_run: time.sleep(0.1) - self.do_work() - if self.do_init: - if self.init(): - self.do_init = False + if self._ready: + self.do_work() + if self._do_init: + self.init() + self._do_init = False def set_filter(self, filter): + self._ready = False self.filter = filter self.session_id = filter.get("SessionId", "NO_SESSION") - # set to false to avoid race condition - # this method is called from the coach thread - self.ready = False - self.do_init = True + self._do_init = True + + def is_initializing(self): + return self._do_init + + def is_ready(self): + return self._ready def init(self): - self.ready = False - self.error = None + self._ready = False + self._error = None self.process_segments = [] self.telemetry = [] @@ -63,39 +66,24 @@ def init(self): except Exception as e: error = f"Error init {self.filter['Driver']} / {self.filter['GameName']}" error += f" / {self.filter['CarModel']}/ {self.filter['TrackCode']} - {e}" - self.error = error + self._error = error self.log_error(error) return False - success = self.init_segments() - if not success: - return False - - self.init_driver() - - # self.telemetry_fields = [ - # "DistanceRoundTrack", - # "Gear", - # "SpeedMs", - # "Throttle", - # "Brake", - # "CurrentLapTime", - # ] - # self.telemetry = {"_time": []} - # for field in self.telemetry_fields: - # self.telemetry[field] = [] - - self.ready = True - return True + if self.init_segments(): + if self.init_driver(): + self._ready = True + return True def init_segments(self) -> bool: """Load the segments from DB.""" fast_lap = FastLap.objects.filter(track=self.track, car=self.car, game=self.game).first() if not fast_lap: - self.error = f"no data found for game {self.filter['GameName']}" - self.error += f" on track {self.filter['TrackCode']}" - self.error += f" in car {self.filter['CarModel']}" - self.log_error(self.error) + error = f"no data found for game {self.filter['GameName']}" + error += f" on track {self.filter['TrackCode']}" + error += f" in car {self.filter['CarModel']}" + self._error = error + self.log_error(error) return False self.log_debug("loading segments for %s %s - %s", self.game, self.track, self.car) @@ -121,15 +109,14 @@ def init_segments(self) -> bool: self.fast_lap = fast_lap # self.log_debug("loaded %s segments", len(self.segments)) - - self.startup_message = "start coaching " - try: - lap_time = self.fast_lap.laps.first().time_human() - self.startup_message += f"for a lap time of {lap_time}" - except Exception: - pass return True + def get_and_reset_error(self): + if self._error: + error = self._error + self._error = None + return error + def features(self, segment, mark="brake"): # search through segements for item in self.track_info: @@ -153,6 +140,7 @@ def sort_segments(self, distance=0): def init_driver(self): self.driver_segments = {} self.driver_data = {} + return True # fast_lap, created = FastLap.objects.get_or_create( # car=self.car, track=self.track, game=self.game, driver=self.driver # ) diff --git a/components/paddock/telemetry/tests/data/responses_test_coach.txt b/components/paddock/telemetry/tests/data/responses_test_coach.txt index 61d9699f..ff0adf58 100644 --- a/components/paddock/telemetry/tests/data/responses_test_coach.txt +++ b/components/paddock/telemetry/tests/data/responses_test_coach.txt @@ -1,4 +1,4 @@ -[('/coach/durandom', 'start coaching for a lap time of 1 minute 37.01 seconds '), +[('/coach/durandom', ['start coaching for a lap time of 1 minute 37.01 seconds ']), ('/coach/durandom', ['{"distance": 447, "message": "80 percent", "priority": 8}']), ('/coach/durandom', ['{"distance": 1077, "message": "40 percent", "priority": 8}']), ('/coach/durandom', ['{"distance": 1208, "message": "brake", "priority": 8}']), diff --git a/components/paddock/telemetry/tests/test_coach.py b/components/paddock/telemetry/tests/test_coach.py index 815830bd..50ffd2ec 100644 --- a/components/paddock/telemetry/tests/test_coach.py +++ b/components/paddock/telemetry/tests/test_coach.py @@ -47,10 +47,11 @@ def test_coach(self): coach.notify(topic, row) if use_threads: print("waiting for history to be ready") - while not history.ready: + while not history._ready: time.sleep(0.1) else: history.init() + history._do_init = False captured_responses = [] try: