smartfleet-simulator 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- smartfleet_simulator-0.1.0/LICENSE +4 -0
- smartfleet_simulator-0.1.0/PKG-INFO +34 -0
- smartfleet_simulator-0.1.0/README.md +13 -0
- smartfleet_simulator-0.1.0/fleet_simulator_pkg/__init__.py +6 -0
- smartfleet_simulator-0.1.0/fleet_simulator_pkg/simulator.py +113 -0
- smartfleet_simulator-0.1.0/fleet_simulator_pkg/telemetry.py +53 -0
- smartfleet_simulator-0.1.0/fleet_simulator_pkg/vehicle.py +68 -0
- smartfleet_simulator-0.1.0/setup.cfg +4 -0
- smartfleet_simulator-0.1.0/setup.py +22 -0
- smartfleet_simulator-0.1.0/smartfleet_simulator.egg-info/PKG-INFO +34 -0
- smartfleet_simulator-0.1.0/smartfleet_simulator.egg-info/SOURCES.txt +12 -0
- smartfleet_simulator-0.1.0/smartfleet_simulator.egg-info/dependency_links.txt +1 -0
- smartfleet_simulator-0.1.0/smartfleet_simulator.egg-info/top_level.txt +1 -0
- smartfleet_simulator-0.1.0/tests/test_simulator.py +85 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: smartfleet-simulator
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Reusable library for simulating vehicle fleet telemetry
|
|
5
|
+
Author: Satvik T M
|
|
6
|
+
Author-email: satvikw007@gmail.com
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Requires-Python: >=3.8
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Dynamic: author
|
|
14
|
+
Dynamic: author-email
|
|
15
|
+
Dynamic: classifier
|
|
16
|
+
Dynamic: description
|
|
17
|
+
Dynamic: description-content-type
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
Dynamic: requires-python
|
|
20
|
+
Dynamic: summary
|
|
21
|
+
|
|
22
|
+
# fleet-simulator-satvik25159712
|
|
23
|
+
|
|
24
|
+
Reusable Python library for simulating delivery vehicle fleet telemetry.
|
|
25
|
+
Generates GPS, speed, fuel and engine temperature readings and posts
|
|
26
|
+
them to AWS API Gateway.
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
pip install fleet-simulator-satvik25159712
|
|
30
|
+
|
|
31
|
+
## Quick start
|
|
32
|
+
from fleet_simulator_pkg import FleetSimulator
|
|
33
|
+
sim = FleetSimulator()
|
|
34
|
+
sim.run(dry_run=True, max_ticks=5)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# fleet-simulator-satvik25159712
|
|
2
|
+
|
|
3
|
+
Reusable Python library for simulating delivery vehicle fleet telemetry.
|
|
4
|
+
Generates GPS, speed, fuel and engine temperature readings and posts
|
|
5
|
+
them to AWS API Gateway.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
pip install fleet-simulator-satvik25159712
|
|
9
|
+
|
|
10
|
+
## Quick start
|
|
11
|
+
from fleet_simulator_pkg import FleetSimulator
|
|
12
|
+
sim = FleetSimulator()
|
|
13
|
+
sim.run(dry_run=True, max_ticks=5)
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""
|
|
2
|
+
simulator.py
|
|
3
|
+
Manages a fleet of Vehicle objects and drives the telemetry loop.
|
|
4
|
+
Sends TelemetryReading objects to AWS API Gateway via HTTP POST.
|
|
5
|
+
"""
|
|
6
|
+
import json
|
|
7
|
+
import time
|
|
8
|
+
import urllib.request
|
|
9
|
+
import urllib.error
|
|
10
|
+
from .vehicle import Vehicle
|
|
11
|
+
from .telemetry import TelemetryReading
|
|
12
|
+
|
|
13
|
+
DEFAULT_FLEET = [
|
|
14
|
+
{"vehicle_id": "VAN-001", "driver_name": "Raghu Bandi", "route_id": "ROUTE-D1"},
|
|
15
|
+
{"vehicle_id": "VAN-002", "driver_name": "Manju Kolar", "route_id": "ROUTE-D2"},
|
|
16
|
+
{"vehicle_id": "VAN-003", "driver_name": "Shubham Dube", "route_id": "ROUTE-D3"},
|
|
17
|
+
{"vehicle_id": "VAN-004", "driver_name": "Pranith Raj", "route_id": "ROUTE-D4"},
|
|
18
|
+
{"vehicle_id": "VAN-005", "driver_name": "Nikhil Bandi", "route_id": "ROUTE-D5"},
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class FleetSimulator:
|
|
23
|
+
"""
|
|
24
|
+
Manages a fleet of vehicles and drives a continuous telemetry loop.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, fleet_config=None, api_url=None, api_key=None):
|
|
28
|
+
"""
|
|
29
|
+
Initialise with a fleet and optional API target.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
fleet_config (list): List of dicts with vehicle_id,
|
|
33
|
+
driver_name, route_id. Uses DEFAULT_FLEET if None.
|
|
34
|
+
api_url (str): API Gateway endpoint URL.
|
|
35
|
+
api_key (str): Optional x-api-key header value.
|
|
36
|
+
"""
|
|
37
|
+
config = fleet_config if fleet_config is not None else DEFAULT_FLEET
|
|
38
|
+
self.vehicles = [
|
|
39
|
+
Vehicle(v["vehicle_id"], v["driver_name"], v["route_id"])
|
|
40
|
+
for v in config
|
|
41
|
+
]
|
|
42
|
+
self.api_url = api_url
|
|
43
|
+
self.api_key = api_key
|
|
44
|
+
|
|
45
|
+
def generate_readings(self):
|
|
46
|
+
"""Advance all vehicles one tick. Returns list of TelemetryReadings."""
|
|
47
|
+
readings = []
|
|
48
|
+
for vehicle in self.vehicles:
|
|
49
|
+
vehicle.update_state()
|
|
50
|
+
readings.append(TelemetryReading.from_vehicle(vehicle))
|
|
51
|
+
return readings
|
|
52
|
+
|
|
53
|
+
def send_reading(self, reading):
|
|
54
|
+
"""
|
|
55
|
+
POST a single TelemetryReading to API Gateway.
|
|
56
|
+
Returns True on HTTP 200/201/202, False on failure.
|
|
57
|
+
"""
|
|
58
|
+
if not self.api_url:
|
|
59
|
+
return False
|
|
60
|
+
payload = json.dumps(reading.to_dict()).encode("utf-8")
|
|
61
|
+
headers = {"Content-Type": "application/json"}
|
|
62
|
+
if self.api_key:
|
|
63
|
+
headers["x-api-key"] = self.api_key
|
|
64
|
+
req = urllib.request.Request(
|
|
65
|
+
self.api_url, data=payload, headers=headers, method="POST"
|
|
66
|
+
)
|
|
67
|
+
try:
|
|
68
|
+
with urllib.request.urlopen(req, timeout=10) as resp:
|
|
69
|
+
return resp.status in (200, 201, 202)
|
|
70
|
+
except urllib.error.URLError as e:
|
|
71
|
+
print(f" [ERROR] {reading.vehicle_id}: {e}")
|
|
72
|
+
return False
|
|
73
|
+
|
|
74
|
+
def run(self, interval_seconds=5.0, max_ticks=None,
|
|
75
|
+
dry_run=False, on_reading=None):
|
|
76
|
+
"""
|
|
77
|
+
Start the simulation loop.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
interval_seconds (float): Pause between ticks.
|
|
81
|
+
max_ticks (int): Stop after this many ticks. None = run forever.
|
|
82
|
+
dry_run (bool): Print to console instead of sending to AWS.
|
|
83
|
+
on_reading (callable): Optional callback per reading.
|
|
84
|
+
"""
|
|
85
|
+
tick = 0
|
|
86
|
+
print(f"[FleetSimulator] Starting — {len(self.vehicles)} vehicles, "
|
|
87
|
+
f"interval={interval_seconds}s, dry_run={dry_run}")
|
|
88
|
+
try:
|
|
89
|
+
while True:
|
|
90
|
+
tick += 1
|
|
91
|
+
readings = self.generate_readings()
|
|
92
|
+
for reading in readings:
|
|
93
|
+
if dry_run or not self.api_url:
|
|
94
|
+
print(f" [tick {tick:04d}] {reading.vehicle_id} | "
|
|
95
|
+
f"speed={reading.speed_kmh:.1f}km/h | "
|
|
96
|
+
f"fuel={reading.fuel_percent:.1f}% | "
|
|
97
|
+
f"temp={reading.engine_temp_c:.1f}C")
|
|
98
|
+
else:
|
|
99
|
+
ok = self.send_reading(reading)
|
|
100
|
+
print(f" [tick {tick:04d}] {reading.vehicle_id} → "
|
|
101
|
+
f"{'OK' if ok else 'FAIL'}")
|
|
102
|
+
if on_reading:
|
|
103
|
+
on_reading(reading)
|
|
104
|
+
if max_ticks and tick >= max_ticks:
|
|
105
|
+
print(f"[FleetSimulator] Done — {max_ticks} ticks.")
|
|
106
|
+
break
|
|
107
|
+
time.sleep(interval_seconds)
|
|
108
|
+
except KeyboardInterrupt:
|
|
109
|
+
print("\n[FleetSimulator] Stopped.")
|
|
110
|
+
|
|
111
|
+
def get_fleet_summary(self):
|
|
112
|
+
"""Return current state of all vehicles as a list of dicts."""
|
|
113
|
+
return [v.to_dict() for v in self.vehicles]
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""
|
|
2
|
+
telemetry.py
|
|
3
|
+
A timestamped snapshot of one vehicle at one point in time.
|
|
4
|
+
This is the payload sent to AWS API Gateway.
|
|
5
|
+
"""
|
|
6
|
+
import datetime
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TelemetryReading:
|
|
10
|
+
"""
|
|
11
|
+
Immutable snapshot of a vehicle's state at a single moment.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def __init__(self, vehicle_id, driver_name, route_id,
|
|
15
|
+
lat, lon, speed_kmh, fuel_percent, engine_temp_c):
|
|
16
|
+
self.vehicle_id = vehicle_id
|
|
17
|
+
self.driver_name = driver_name
|
|
18
|
+
self.route_id = route_id
|
|
19
|
+
self.timestamp = datetime.datetime.now(
|
|
20
|
+
datetime.timezone.utc).isoformat()
|
|
21
|
+
self.lat = lat
|
|
22
|
+
self.lon = lon
|
|
23
|
+
self.speed_kmh = speed_kmh
|
|
24
|
+
self.fuel_percent = fuel_percent
|
|
25
|
+
self.engine_temp_c = engine_temp_c
|
|
26
|
+
|
|
27
|
+
@classmethod
|
|
28
|
+
def from_vehicle(cls, vehicle):
|
|
29
|
+
"""Create a reading directly from a Vehicle instance."""
|
|
30
|
+
return cls(
|
|
31
|
+
vehicle_id = vehicle.vehicle_id,
|
|
32
|
+
driver_name = vehicle.driver_name,
|
|
33
|
+
route_id = vehicle.route_id,
|
|
34
|
+
lat = vehicle.lat,
|
|
35
|
+
lon = vehicle.lon,
|
|
36
|
+
speed_kmh = vehicle.speed_kmh,
|
|
37
|
+
fuel_percent = vehicle.fuel_percent,
|
|
38
|
+
engine_temp_c = vehicle.engine_temp_c,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
def to_dict(self):
|
|
42
|
+
"""Serialise to a flat dict for JSON encoding."""
|
|
43
|
+
return {
|
|
44
|
+
"vehicle_id": self.vehicle_id,
|
|
45
|
+
"driver_name": self.driver_name,
|
|
46
|
+
"route_id": self.route_id,
|
|
47
|
+
"timestamp": self.timestamp,
|
|
48
|
+
"lat": self.lat,
|
|
49
|
+
"lon": self.lon,
|
|
50
|
+
"speed_kmh": self.speed_kmh,
|
|
51
|
+
"fuel_percent": self.fuel_percent,
|
|
52
|
+
"engine_temp_c": self.engine_temp_c,
|
|
53
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""
|
|
2
|
+
vehicle.py
|
|
3
|
+
This file represents a single delivery vehicle with persistent state.
|
|
4
|
+
Each vehicle maintains its own position, speed, fuel and engine
|
|
5
|
+
temperature across telemetry ticks.
|
|
6
|
+
"""
|
|
7
|
+
import random
|
|
8
|
+
|
|
9
|
+
DEFAULT_LAT = 53.3498
|
|
10
|
+
DEFAULT_LON = -6.2603
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Vehicle:
|
|
14
|
+
"""
|
|
15
|
+
A single fleet vehicle with state that updates each tick.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, vehicle_id, driver_name, route_id,
|
|
19
|
+
start_lat=DEFAULT_LAT, start_lon=DEFAULT_LON):
|
|
20
|
+
self.vehicle_id = vehicle_id
|
|
21
|
+
self.driver_name = driver_name
|
|
22
|
+
self.route_id = route_id
|
|
23
|
+
self.lat = start_lat
|
|
24
|
+
self.lon = start_lon
|
|
25
|
+
self.speed_kmh = 0.0
|
|
26
|
+
self.fuel_percent = random.uniform(60.0, 100.0)
|
|
27
|
+
self.engine_temp_c = random.uniform(75.0, 95.0)
|
|
28
|
+
# Small random fault probability so some vehicles generate alerts
|
|
29
|
+
self._fault_probability = random.uniform(0.0, 0.15)
|
|
30
|
+
|
|
31
|
+
def update_state(self):
|
|
32
|
+
"""
|
|
33
|
+
Advance vehicle state by one telemetry tick.
|
|
34
|
+
Simulates realistic GPS drift, speed changes,
|
|
35
|
+
fuel drain and occasional engine temperature spikes.
|
|
36
|
+
"""
|
|
37
|
+
# Simulates a GPS movement
|
|
38
|
+
self.lat += random.uniform(-0.005, 0.005)
|
|
39
|
+
self.lon += random.uniform(-0.005, 0.005)
|
|
40
|
+
|
|
41
|
+
# speed transitions
|
|
42
|
+
speed_delta = random.uniform(-15, 15)
|
|
43
|
+
self.speed_kmh = max(0.0, min(110.0, self.speed_kmh + speed_delta))
|
|
44
|
+
|
|
45
|
+
# Fuel drains proportionally to speed
|
|
46
|
+
fuel_drain = (self.speed_kmh / 100.0) * random.uniform(0.05, 0.2)
|
|
47
|
+
self.fuel_percent = max(5.0, self.fuel_percent - fuel_drain)
|
|
48
|
+
|
|
49
|
+
# Engine temp — occasionally made to spike on faulty vehicles
|
|
50
|
+
if random.random() < self._fault_probability:
|
|
51
|
+
self.engine_temp_c = random.uniform(105.0, 120.0)
|
|
52
|
+
else:
|
|
53
|
+
temp_delta = random.uniform(-2, 2)
|
|
54
|
+
self.engine_temp_c = max(75.0, min(95.0,
|
|
55
|
+
self.engine_temp_c + temp_delta))
|
|
56
|
+
|
|
57
|
+
def to_dict(self):
|
|
58
|
+
"""Serialise current state to a dict for JSON encoding."""
|
|
59
|
+
return {
|
|
60
|
+
"vehicle_id": self.vehicle_id,
|
|
61
|
+
"driver_name": self.driver_name,
|
|
62
|
+
"route_id": self.route_id,
|
|
63
|
+
"lat": round(self.lat, 6),
|
|
64
|
+
"lon": round(self.lon, 6),
|
|
65
|
+
"speed_kmh": round(self.speed_kmh, 2),
|
|
66
|
+
"fuel_percent": round(self.fuel_percent, 2),
|
|
67
|
+
"engine_temp_c": round(self.engine_temp_c, 2),
|
|
68
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import setuptools
|
|
2
|
+
|
|
3
|
+
with open("README.md", "r", encoding="utf-8") as fh:
|
|
4
|
+
long_description = fh.read()
|
|
5
|
+
|
|
6
|
+
setuptools.setup(
|
|
7
|
+
name="smartfleet-simulator",
|
|
8
|
+
version="0.1.0",
|
|
9
|
+
author="Satvik T M",
|
|
10
|
+
author_email="satvikw007@gmail.com",
|
|
11
|
+
description="Reusable library for simulating vehicle fleet telemetry",
|
|
12
|
+
long_description=long_description,
|
|
13
|
+
long_description_content_type="text/markdown",
|
|
14
|
+
packages=setuptools.find_packages(),
|
|
15
|
+
install_requires=[],
|
|
16
|
+
classifiers=[
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
],
|
|
21
|
+
python_requires=">=3.8",
|
|
22
|
+
)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: smartfleet-simulator
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Reusable library for simulating vehicle fleet telemetry
|
|
5
|
+
Author: Satvik T M
|
|
6
|
+
Author-email: satvikw007@gmail.com
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Requires-Python: >=3.8
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Dynamic: author
|
|
14
|
+
Dynamic: author-email
|
|
15
|
+
Dynamic: classifier
|
|
16
|
+
Dynamic: description
|
|
17
|
+
Dynamic: description-content-type
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
Dynamic: requires-python
|
|
20
|
+
Dynamic: summary
|
|
21
|
+
|
|
22
|
+
# fleet-simulator-satvik25159712
|
|
23
|
+
|
|
24
|
+
Reusable Python library for simulating delivery vehicle fleet telemetry.
|
|
25
|
+
Generates GPS, speed, fuel and engine temperature readings and posts
|
|
26
|
+
them to AWS API Gateway.
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
pip install fleet-simulator-satvik25159712
|
|
30
|
+
|
|
31
|
+
## Quick start
|
|
32
|
+
from fleet_simulator_pkg import FleetSimulator
|
|
33
|
+
sim = FleetSimulator()
|
|
34
|
+
sim.run(dry_run=True, max_ticks=5)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
setup.py
|
|
4
|
+
fleet_simulator_pkg/__init__.py
|
|
5
|
+
fleet_simulator_pkg/simulator.py
|
|
6
|
+
fleet_simulator_pkg/telemetry.py
|
|
7
|
+
fleet_simulator_pkg/vehicle.py
|
|
8
|
+
smartfleet_simulator.egg-info/PKG-INFO
|
|
9
|
+
smartfleet_simulator.egg-info/SOURCES.txt
|
|
10
|
+
smartfleet_simulator.egg-info/dependency_links.txt
|
|
11
|
+
smartfleet_simulator.egg-info/top_level.txt
|
|
12
|
+
tests/test_simulator.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
fleet_simulator_pkg
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""
|
|
2
|
+
test_simulator.py
|
|
3
|
+
Unit tests for fleet_simulator_pkg.
|
|
4
|
+
Run with: python3 -m pytest tests/ -v
|
|
5
|
+
"""
|
|
6
|
+
from fleet_simulator_pkg.vehicle import Vehicle
|
|
7
|
+
from fleet_simulator_pkg.telemetry import TelemetryReading
|
|
8
|
+
from fleet_simulator_pkg.simulator import FleetSimulator
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TestVehicle:
|
|
12
|
+
|
|
13
|
+
def test_initialises_with_correct_id(self):
|
|
14
|
+
v = Vehicle("VAN-001", "Raghu Bandi", "ROUTE-D1")
|
|
15
|
+
assert v.vehicle_id == "VAN-001"
|
|
16
|
+
|
|
17
|
+
def test_fuel_starts_within_range(self):
|
|
18
|
+
v = Vehicle("VAN-001", "Raghu Bandi", "ROUTE-D1")
|
|
19
|
+
assert 60.0 <= v.fuel_percent <= 100.0
|
|
20
|
+
|
|
21
|
+
def test_fuel_drains_after_updates(self):
|
|
22
|
+
v = Vehicle("VAN-001", "Raghu Bandi", "ROUTE-D1")
|
|
23
|
+
initial = v.fuel_percent
|
|
24
|
+
for _ in range(20):
|
|
25
|
+
v.update_state()
|
|
26
|
+
assert v.fuel_percent <= initial
|
|
27
|
+
|
|
28
|
+
def test_speed_stays_within_bounds(self):
|
|
29
|
+
v = Vehicle("VAN-001", "Raghu Bandi", "ROUTE-D1")
|
|
30
|
+
for _ in range(50):
|
|
31
|
+
v.update_state()
|
|
32
|
+
assert 0.0 <= v.speed_kmh <= 110.0
|
|
33
|
+
|
|
34
|
+
def test_to_dict_has_all_keys(self):
|
|
35
|
+
v = Vehicle("VAN-001", "Raghu Bandi", "ROUTE-D1")
|
|
36
|
+
keys = set(v.to_dict().keys())
|
|
37
|
+
assert keys == {"vehicle_id", "driver_name", "route_id",
|
|
38
|
+
"lat", "lon", "speed_kmh",
|
|
39
|
+
"fuel_percent", "engine_temp_c"}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class TestTelemetryReading:
|
|
43
|
+
|
|
44
|
+
def test_from_vehicle_captures_id(self):
|
|
45
|
+
v = Vehicle("VAN-002", "Manju Kolar", "ROUTE-D2")
|
|
46
|
+
r = TelemetryReading.from_vehicle(v)
|
|
47
|
+
assert r.vehicle_id == "VAN-002"
|
|
48
|
+
|
|
49
|
+
def test_timestamp_is_utc_iso(self):
|
|
50
|
+
v = Vehicle("VAN-002", "Manju Kolar", "ROUTE-D2")
|
|
51
|
+
r = TelemetryReading.from_vehicle(v)
|
|
52
|
+
assert "T" in r.timestamp
|
|
53
|
+
assert "+" in r.timestamp or "Z" in r.timestamp
|
|
54
|
+
|
|
55
|
+
def test_to_dict_includes_timestamp(self):
|
|
56
|
+
v = Vehicle("VAN-003", "Shubham Dube", "ROUTE-D3")
|
|
57
|
+
d = TelemetryReading.from_vehicle(v).to_dict()
|
|
58
|
+
assert "timestamp" in d
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class TestFleetSimulator:
|
|
62
|
+
|
|
63
|
+
def test_default_fleet_has_five_vehicles(self):
|
|
64
|
+
sim = FleetSimulator()
|
|
65
|
+
assert len(sim.vehicles) == 5
|
|
66
|
+
|
|
67
|
+
def test_custom_fleet_config(self):
|
|
68
|
+
sim = FleetSimulator(fleet_config=[
|
|
69
|
+
{"vehicle_id": "T-01", "driver_name": "Arun", "route_id": "R1"},
|
|
70
|
+
])
|
|
71
|
+
assert len(sim.vehicles) == 1
|
|
72
|
+
assert sim.vehicles[0].vehicle_id == "T-01"
|
|
73
|
+
|
|
74
|
+
def test_generate_readings_count(self):
|
|
75
|
+
sim = FleetSimulator()
|
|
76
|
+
assert len(sim.generate_readings()) == 5
|
|
77
|
+
|
|
78
|
+
def test_callback_fires_for_each_reading(self):
|
|
79
|
+
sim = FleetSimulator()
|
|
80
|
+
collected = []
|
|
81
|
+
sim.run(interval_seconds=0, max_ticks=2,
|
|
82
|
+
dry_run=True, on_reading=lambda r: collected.append(r))
|
|
83
|
+
assert len(collected) == 10
|
|
84
|
+
# 5 vehicles x 2 ticks
|
|
85
|
+
|