Variables in SimulationThread now consistently distinguish real vs

simulation time in names.
This commit is contained in:
Chris Davoren 2023-10-21 16:38:12 +10:00
parent 82a40b5143
commit e5060b33c5
3 changed files with 30 additions and 37 deletions

View File

@ -82,11 +82,3 @@ Project settings are in the standard Django location, `trafflights/settings.py`.
1. **SIMULATION_START_TIME**: The internal time at which the simulation starts, in ISO text format. By default this is 07:59 for demonstration purposes. 1. **SIMULATION_START_TIME**: The internal time at which the simulation starts, in ISO text format. By default this is 07:59 for demonstration purposes.
1. **SIMULATION_TIME_FACTOR**: The time "acceleration" factor (floating point) for the simulation. A factor of 1.0 will run in real-time, and higher factors will run faster. Default is 4.0. 1. **SIMULATION_TIME_FACTOR**: The time "acceleration" factor (floating point) for the simulation. A factor of 1.0 will run in real-time, and higher factors will run faster. Default is 4.0.
# TODO List
1. Add instructions for installation/execution to README (including pre-requisites)
1. Add configuration options for periods data file, start time, and time factor
1. Change print statements to formal logging
1. Remaining refinements for frontend (font, remove debug visual aids, time remaining)
1. Move algorithm testing file to simulation directory, and change to use SimulationThread
1. Consider variable naming to better delineate similation vs real time variables

View File

@ -45,8 +45,8 @@ class SimulationThread(threading.Thread):
self.mutex = threading.Lock() self.mutex = threading.Lock()
self.periods = periods self.periods = periods
self.start_time = start_time self.start_simulation_time = start_time
self.run_time = run_time self.run_real_time = run_time
self.time_factor = time_factor self.time_factor = time_factor
self.verbose = verbose self.verbose = verbose
@ -55,9 +55,11 @@ class SimulationThread(threading.Thread):
self.current_period_index = 0 self.current_period_index = 0
self.current_state_index = 0 self.current_state_index = 0
self.next_changeover_time = None
self.simulation_start_datetime = None self.next_changeover_real_time = None
self.start_simulation_datetime = None
self.current_simulation_datetime = None
# For convenience, convert all ISO format times to datetime.time objects # For convenience, convert all ISO format times to datetime.time objects
for period in self.periods: for period in self.periods:
@ -78,13 +80,12 @@ class SimulationThread(threading.Thread):
""" """
Resets the simulation time to the provided time. This is for testing purposes; the current state is IGNORED and the sequence immediately resets to the beginning. Resets the simulation time to the provided time. This is for testing purposes; the current state is IGNORED and the sequence immediately resets to the beginning.
""" """
new_datetime = datetime.datetime.fromisoformat("1900-01-01 " + new_time.isoformat()) new_simulation_datetime = datetime.datetime.fromisoformat("1900-01-01 " + new_time.isoformat())
self.mutex.acquire() self.mutex.acquire()
print() print()
# print("Forcing internal time change from {} to {}".format(self.simulation_start_datetime.time().isoformat(), new_time.isoformat())) self.current_period_index = self._get_period_index(self.periods, new_simulation_datetime.time())
self.current_period_index = self._get_period_index(self.periods, new_datetime.time())
self.current_state_index = 0 self.current_state_index = 0
self.next_changeover_time = time.time() + (self.periods[self.current_period_index]["states"][self.current_state_index]["duration"] / self.time_factor) self.next_changeover_real_time = time.time() + (self.periods[self.current_period_index]["states"][self.current_state_index]["duration"] / self.time_factor)
self.mutex.release() self.mutex.release()
def get_snapshot(self): def get_snapshot(self):
@ -96,11 +97,11 @@ class SimulationThread(threading.Thread):
"current_period_name": self.periods[self.current_period_index]["name"], "current_period_name": self.periods[self.current_period_index]["name"],
"current_period_verbose_name": self.periods[self.current_period_index]["verbose-name"], "current_period_verbose_name": self.periods[self.current_period_index]["verbose-name"],
"current_period_index": self.current_period_index, "current_period_index": self.current_period_index,
"current_state_index" : self.current_state_index, "current_state_index": self.current_state_index,
"current_state_data": self.periods[self.current_period_index]["states"][self.current_state_index], "current_state_data": self.periods[self.current_period_index]["states"][self.current_state_index],
"next_changeover_time": self.next_changeover_time, "next_changeover_real_time": self.next_changeover_real_time,
"simulation_time": self.current_simulation_datetime.time(), "simulation_time": self.current_simulation_datetime.time(),
"time_remaining": (self.next_changeover_time - time.time()) * self.time_factor, "time_remaining": (self.next_changeover_real_time - time.time()) * self.time_factor,
} }
self.mutex.release() self.mutex.release()
return return_data return return_data
@ -128,30 +129,29 @@ class SimulationThread(threading.Thread):
- signal_stop() has been called - signal_stop() has been called
- The executing program finishes (daemon=True) - The executing program finishes (daemon=True)
""" """
real_start_time = time.time() start_real_time = time.time()
self.simulation_start_datetime = None if self.start_simulation_time is None:
if self.start_time is None: self.start_simulation_datetime = datetime.datetime.now()
self.simulation_start_datetime = datetime.datetime.now()
else: else:
self.simulation_start_datetime = datetime.datetime.fromisoformat( self.start_simulation_datetime = datetime.datetime.fromisoformat(
"1900-01-01 " + self.start_time.isoformat()) "1900-01-01 " + self.start_simulation_time.isoformat())
self.current_period_index = self._get_period_index(self.simulation_start_datetime.time()) self.current_period_index = self._get_period_index(self.start_simulation_datetime.time())
self.current_state_index = 0 self.current_state_index = 0
self.next_changeover_time = real_start_time + (self.periods[self.current_period_index]["states"][self.current_state_index]["duration"] / self.time_factor) self.next_changeover_real_time = start_real_time + (self.periods[self.current_period_index]["states"][self.current_state_index]["duration"] / self.time_factor)
if self.verbose: if self.verbose:
print("Starting with simulation time {}".format(self.simulation_start_datetime.time())) print("Starting with simulation time {}".format(self.start_simulation_datetime.time()))
print("Initial state data: {}".format(self.periods[self.current_period_index]["states"][self.current_state_index])) print("Initial state data: {}".format(self.periods[self.current_period_index]["states"][self.current_state_index]))
if self.run_time is None: if self.run_real_time is None:
print("No simulation time limit.") print("No simulation time limit.")
else: else:
print("Simulation duration (in real second(s)): {}".format(self.run_time)) print("Simulation duration (in real second(s)): {}".format(self.run_real_time))
print("Time compression factor: {}".format(self.time_factor)) print("Time compression factor: {}".format(self.time_factor))
while (self.run_time is None) or (self.run_time is not None and time.time() < real_start_time + self.run_time): while (self.run_real_time is None) or (self.run_real_time is not None and time.time() < start_real_time + self.run_real_time):
self.mutex.acquire() self.mutex.acquire()
if not self.running: if not self.running:
@ -159,10 +159,10 @@ class SimulationThread(threading.Thread):
break break
now = time.time() now = time.time()
self.current_simulation_datetime = self.simulation_start_datetime + datetime.timedelta(seconds=int((now - real_start_time) * self.time_factor)) self.current_simulation_datetime = self.start_simulation_datetime + datetime.timedelta(seconds=int((now - start_real_time) * self.time_factor))
timed_period_index = self._get_period_index(self.current_simulation_datetime.time()) timed_period_index = self._get_period_index(self.current_simulation_datetime.time())
if now >= self.next_changeover_time: if now >= self.next_changeover_real_time:
timed_period_index = self._get_period_index(self.current_simulation_datetime.time()) timed_period_index = self._get_period_index(self.current_simulation_datetime.time())
if timed_period_index != self.current_period_index and self.current_state_index == len(self.periods[self.current_period_index]["states"])-1: if timed_period_index != self.current_period_index and self.current_state_index == len(self.periods[self.current_period_index]["states"])-1:
if self.verbose: if self.verbose:
@ -192,7 +192,8 @@ class SimulationThread(threading.Thread):
self.current_state_index = next_cycle_index self.current_state_index = next_cycle_index
self.next_changeover_time = self.next_changeover_time + (self.periods[self.current_period_index]["states"][self.current_state_index]["duration"] / self.time_factor) # Ensure this calculation is done based on the PREVIOUS scheduled time to avoid "timing drift"
self.next_changeover_real_time = self.next_changeover_real_time + (self.periods[self.current_period_index]["states"][self.current_state_index]["duration"] / self.time_factor)
else: else:
if self.verbose: if self.verbose:
print(".", end="") print(".", end="")

View File

@ -105,7 +105,7 @@
while (true) while (true)
{ {
await sleep(250); await sleep(250);
console.log("Tick"); // console.log("Tick");
req.open("GET", "/frontend/status", false); req.open("GET", "/frontend/status", false);
// Note try/catch for exceptions is the only way to account for net::ERR_CONNECTION_REFUSED - this is NOT dealt with by XMLHttpRequest event hooks. // Note try/catch for exceptions is the only way to account for net::ERR_CONNECTION_REFUSED - this is NOT dealt with by XMLHttpRequest event hooks.