Initial commit of files.

This commit is contained in:
Chris Davoren 2023-10-18 13:19:32 +10:00
commit eb897e18ae
4 changed files with 216 additions and 0 deletions

0
.gitignore vendored Normal file
View File

31
README.md Normal file
View File

@ -0,0 +1,31 @@
# Traffic Light System
## Problem Description
### Summary
This test is a take home test, please return once completed
### Requirements
In this test we would like you to implement a traffic light system. We are required to have 4 sets of lights, as follows.
- Lights 1: Traffic is travelling south
- Lights 2: Traffic is travelling west
- Lights 3: Traffic is travelling north
- Lights 4: Traffic is travelling east
The lights in which traffic is travelling on the same axis can be green at the same time. During normal hours all lights stay green for 20 seconds, but during peak times north and south lights are green for 40 seconds while west and east are green for 10 seconds. Peak hours are 0800 to 1000 and 1700 to 1900. Yellow lights are shown for 5 seconds before red lights are shown. Red lights stay on until the cross-traffic is red for at least 4 seconds, once a red light goes off then the green is shown for the required time(eg the sequence is reset).
Bonus: At this intersection north bound traffic has a green right-turn signal, which stops the south bound traffic and allows north bound traffic to turn right. This is green at the end of north/south green light and stays green for 10 seconds. During this time north bound is green, north right-turn is green and all other lights are red.
### Implementation/Outcomes
1. Implement a front-end and backend (you can use dotnet new templates of your choice)
2. The backend will contain the logic and state of the running traffic lights. The front-end will be a visual representation of the traffic lights, with the data served from the backend.
3. Theres no need to have a perfect design on the front end, something simple and functional is fine (unless this is an area of strength you would like to show off). Noting* we will review the client side code.
4. Theres no need to implement entity framework (or similar) to store the data in a database, a in-memory store is fine
5. Code needs to follow architecture & best practices for enterprise grade systems
Note: Code will be evaluated not just for function, but on the quality of the code.

108
state-read.py Normal file
View File

@ -0,0 +1,108 @@
#!/usr/bin/env python3
import sys
import json
import datetime
import time
def get_state_index(states, test_time : datetime.time):
state_result = None
for i, state in enumerate(states):
if i == len(states) - 1:
return(i)
if test_time >= state["timestart"] and test_time < states[i+1]["timestart"]:
return(i)
# Should NOT be reached since last state assumed to last until midnight
# TODO: Raise exception?
return None
def simulate(states, start_time, run_time=90, time_factor=1.0):
simulation_start_time = time.time()
simulation_start_datetime = datetime.datetime.fromisoformat("1900-01-01 " + start_time.isoformat())
current_state_index = get_state_index(states, simulation_start_datetime.time())
current_cycle_index = 0
next_changeover_time = simulation_start_time + (states[current_state_index]["cycles"][current_cycle_index]["duration"] / time_factor)
print("Starting with simulation time {}".format(simulation_start_datetime.time()))
print("Initial data: {}".format(states[current_state_index]["cycles"][current_cycle_index]))
print("Starting simulation of {} second(s) with time factor {}".format(run_time, time_factor))
while (run_time is None) or (run_time is not None and time.time() < simulation_start_time + run_time):
now = time.time()
current_simulation_datetime = simulation_start_datetime + datetime.timedelta(seconds=int((now - simulation_start_time) * time_factor))
timed_state_index = get_state_index(states, current_simulation_datetime.time())
if now >= next_changeover_time:
timed_state_index = get_state_index(states, current_simulation_datetime.time())
if timed_state_index != current_state_index and current_cycle_index == len(states[current_state_index]["cycles"])-1:
# Safe to change states
print()
print("{} : Changing STATES from {} to {}".format(current_simulation_datetime.time(), states[current_state_index]["name"], states[timed_state_index]["name"]))
print(" Old cycle data: {}".format(states[current_state_index]["cycles"][current_cycle_index]))
current_state_index = timed_state_index
current_cycle_index = 0
print(" New cycle data: {}".format(states[current_state_index]["cycles"][current_cycle_index]))
print(" Next changeover in {} second(s)".format(states[current_state_index]["cycles"][current_cycle_index]["duration"]))
else:
next_cycle_index = (current_cycle_index + 1) % len(states[current_state_index]["cycles"])
print()
print("{} : Changing cycle from {} to {}".format(current_simulation_datetime.time(), current_cycle_index, next_cycle_index))
if current_state_index != timed_state_index:
print(" [state change pending]")
print(" Old cycle data: {}".format(states[current_state_index]["cycles"][current_cycle_index]))
print(" New cycle data: {}".format(states[current_state_index]["cycles"][next_cycle_index]))
print(" Next changeover in {} second(s)".format(states[current_state_index]["cycles"][next_cycle_index]["duration"]))
current_cycle_index = next_cycle_index
next_changeover_time = next_changeover_time + (states[current_state_index]["cycles"][current_cycle_index]["duration"] / time_factor)
else:
print(".", end="")
sys.stdout.flush()
time.sleep(1.0 / time_factor)
def main():
states = json.load(open("states.json"))
# Essential due to assumed ordering further below
states.sort(key=lambda x : x["timestart"])
for state in states:
time = datetime.time.fromisoformat(state["timestart"])
print("Name: {}\t\tTime start: {}".format(state["name"], time))
print(" Number of cycles: {}".format(len(state["cycles"])))
state["timestart"] = datetime.time.fromisoformat(state["timestart"])
test_times = [
"00:00:00",
"07:59:59",
"08:00:00",
"09:59:59",
"10:00:00",
"16:59:59",
"17:00:00",
"18:59:59",
"19:00:00",
"23:59:59",
]
for test_time in test_times:
test_time_obj = datetime.time.fromisoformat(test_time)
print("State matching time {} is '{}'".format(test_time, states[get_state_index(states, test_time_obj)]["name"]))
simulate(states, datetime.time.fromisoformat("07:59"), 60, 4.0)
if __name__ == '__main__':
main()

77
states.json Normal file
View File

@ -0,0 +1,77 @@
[
{
"name" : "offpeak-morning",
"timestart" : "00:00",
"cycles" : [
{ "duration" : 20, "north" : "green", "south" : "green", "east" : "red", "west" : "red", "north-right" : "red" },
{ "duration" : 4, "north" : "green", "south" : "amber", "east" : "red", "west" : "red", "north-right" : "red" },
{ "duration" : 2, "north" : "green", "south" : "red", "east" : "red", "west" : "red", "north-right" : "red" },
{ "duration" : 10, "north" : "green", "south" : "red", "east" : "red", "west" : "red", "north-right" : "green" },
{ "duration" : 4, "north" : "amber", "south" : "red", "east" : "red", "west" : "red", "north-right" : "amber" },
{ "duration" : 2, "north" : "red", "south" : "red", "east" : "red", "west" : "red", "north-right" : "red" },
{ "duration" : 20, "north" : "red", "south" : "red", "east" : "green", "west" : "green", "north-right" : "red" },
{ "duration" : 4, "north" : "red", "south" : "red", "east" : "amber", "west" : "amber", "north-right" : "red" },
{ "duration" : 2, "north" : "amber", "south" : "red", "east" : "red", "west" : "red", "north-right" : "red" }
]
},
{
"name" : "peak-morning",
"timestart" : "08:00",
"cycles" : [
{ "duration" : 40, "north" : "green", "south" : "green", "east" : "red", "west" : "red", "north-right" : "red" },
{ "duration" : 4, "north" : "green", "south" : "amber", "east" : "red", "west" : "red", "north-right" : "red" },
{ "duration" : 2, "north" : "green", "south" : "red", "east" : "red", "west" : "red", "north-right" : "red" },
{ "duration" : 10, "north" : "green", "south" : "red", "east" : "red", "west" : "red", "north-right" : "green" },
{ "duration" : 4, "north" : "amber", "south" : "red", "east" : "red", "west" : "red", "north-right" : "amber" },
{ "duration" : 2, "north" : "red", "south" : "red", "east" : "red", "west" : "red", "north-right" : "red" },
{ "duration" : 40, "north" : "red", "south" : "red", "east" : "green", "west" : "green", "north-right" : "red" },
{ "duration" : 4, "north" : "red", "south" : "red", "east" : "amber", "west" : "amber", "north-right" : "red" },
{ "duration" : 2, "north" : "amber", "south" : "red", "east" : "red", "west" : "red", "north-right" : "red" }
]
},
{
"name" : "offpeak-middle",
"timestart" : "10:00",
"cycles" : [
{ "duration" : 20, "north" : "green", "south" : "green", "east" : "red", "west" : "red", "north-right" : "red" },
{ "duration" : 4, "north" : "green", "south" : "amber", "east" : "red", "west" : "red", "north-right" : "red" },
{ "duration" : 2, "north" : "green", "south" : "red", "east" : "red", "west" : "red", "north-right" : "red" },
{ "duration" : 10, "north" : "green", "south" : "red", "east" : "red", "west" : "red", "north-right" : "green" },
{ "duration" : 4, "north" : "amber", "south" : "red", "east" : "red", "west" : "red", "north-right" : "amber" },
{ "duration" : 2, "north" : "red", "south" : "red", "east" : "red", "west" : "red", "north-right" : "red" },
{ "duration" : 20, "north" : "red", "south" : "red", "east" : "green", "west" : "green", "north-right" : "red" },
{ "duration" : 4, "north" : "red", "south" : "red", "east" : "amber", "west" : "amber", "north-right" : "red" },
{ "duration" : 2, "north" : "amber", "south" : "red", "east" : "red", "west" : "red", "north-right" : "red" }
]
},
{
"name" : "peak-afternoon",
"timestart" : "17:00",
"cycles" : [
{ "duration" : 40, "north" : "green", "south" : "green", "east" : "red", "west" : "red", "north-right" : "red" },
{ "duration" : 4, "north" : "green", "south" : "amber", "east" : "red", "west" : "red", "north-right" : "red" },
{ "duration" : 2, "north" : "green", "south" : "red", "east" : "red", "west" : "red", "north-right" : "red" },
{ "duration" : 10, "north" : "green", "south" : "red", "east" : "red", "west" : "red", "north-right" : "green" },
{ "duration" : 4, "north" : "amber", "south" : "red", "east" : "red", "west" : "red", "north-right" : "amber" },
{ "duration" : 2, "north" : "red", "south" : "red", "east" : "red", "west" : "red", "north-right" : "red" },
{ "duration" : 40, "north" : "red", "south" : "red", "east" : "green", "west" : "green", "north-right" : "red" },
{ "duration" : 4, "north" : "red", "south" : "red", "east" : "amber", "west" : "amber", "north-right" : "red" },
{ "duration" : 2, "north" : "amber", "south" : "red", "east" : "red", "west" : "red", "north-right" : "red" }
]
},
{
"name" : "offpeak-evening",
"timestart" : "19:00",
"cycles" : [
{ "duration" : 20, "north" : "green", "south" : "green", "east" : "red", "west" : "red", "north-right" : "red" },
{ "duration" : 4, "north" : "green", "south" : "amber", "east" : "red", "west" : "red", "north-right" : "red" },
{ "duration" : 2, "north" : "green", "south" : "red", "east" : "red", "west" : "red", "north-right" : "red" },
{ "duration" : 10, "north" : "green", "south" : "red", "east" : "red", "west" : "red", "north-right" : "green" },
{ "duration" : 4, "north" : "amber", "south" : "red", "east" : "red", "west" : "red", "north-right" : "amber" },
{ "duration" : 2, "north" : "red", "south" : "red", "east" : "red", "west" : "red", "north-right" : "red" },
{ "duration" : 20, "north" : "red", "south" : "red", "east" : "green", "west" : "green", "north-right" : "red" },
{ "duration" : 4, "north" : "red", "south" : "red", "east" : "amber", "west" : "amber", "north-right" : "red" },
{ "duration" : 2, "north" : "amber", "south" : "red", "east" : "red", "west" : "red", "north-right" : "red" }
]
}
]