99 lines
7.5 KiB
Python
99 lines
7.5 KiB
Python
import sys
|
|
import AssignmentData
|
|
import datetime
|
|
from MyHashTable import DerekHashTable
|
|
from DeliveryObject import DeliveryObject
|
|
from DeliveryObject import deliveryStatus
|
|
|
|
class Truck:
|
|
def __init__(self, truckId: str):
|
|
self.id = truckId # This trucks ID
|
|
self.packages: list[DeliveryObject] = [] # The packages on the truck
|
|
self.hasLoad: bool = False # Status of if the truck was loaded
|
|
self.mileage = 0.0 # Mileage counter
|
|
self.deliveryCounter = 0 # Keep track of the delivery counter
|
|
self.current_location = "HUB" # Current location of the truck
|
|
self.speed = 18 # Set from the project instructions
|
|
self.startTime: datetime.time | None = None # The trucks start time
|
|
|
|
def addPkg(self, pkg: DeliveryObject): # Adds a package to the truck
|
|
if len(self.packages) < 16: # Check if we can fit another package
|
|
pkg.timeStart = self.getTimeOfDay() # Set the start time for the pkg
|
|
pkg.truckId = self.id # Set the truck ID for the end menu
|
|
pkg.status = deliveryStatus.IN_ROUTE # The package is on the truck
|
|
self.packages.append(pkg) # Add the package
|
|
else:
|
|
print("to many packages loaded on the truck") # If there are too many packages
|
|
sys.exit() # Crash the program
|
|
|
|
def getDriveTime(self) -> int: # Usefull outside this class
|
|
hourPercent = self.mileage / self.speed # Calculate the hour percentage
|
|
rounededMin = int(60 * hourPercent) # Average that into minutes
|
|
return rounededMin
|
|
|
|
def getTimeOfDay(self) -> datetime.time: # Get the time of day using offsets
|
|
truckStartMins = (self.startTime.hour * 60) + self.startTime.minute # Get the minutes past midnight when the truck left
|
|
offsetMinutes = truckStartMins + self.getDriveTime() # Add on the number of minutes the truck as been driving
|
|
pkgHours = (offsetMinutes // 60) % 24 # pull hours back out
|
|
pkgMins = offsetMinutes % 60 # pull minutes back out
|
|
return datetime.time(pkgHours, pkgMins) # return the time of day
|
|
|
|
def deliverPkgs(self): # Delivers any packeges that match the current location
|
|
for i in range(len(self.packages) - 1, -1, -1): # Loop backwards so removals dont index shift
|
|
pkg = self.packages[i]
|
|
if pkg.address in self.current_location: # Check if the package is at the correct address
|
|
if ("Wrong address listed" not in pkg.notes or
|
|
self.getTimeOfDay() >= datetime.time(10, 30)): # Wait for corrected shipping address
|
|
pkg.status = deliveryStatus.DELIVERED # Mark as delivered
|
|
pkg.timeEnd = self.getTimeOfDay() # Mark the delivered time
|
|
self.deliveryCounter += 1 # Incriment the delivery counter
|
|
pkg.deliveryNumber = self.deliveryCounter # Mark the package number
|
|
self.packages.pop(i)
|
|
|
|
def getMatrixIndex(self, address_string: str) -> int: # Helper function to get x or y offset in the distance table
|
|
for i, full_name in enumerate(AssignmentData.xyNames2): # Loop through the names in the list
|
|
if address_string in full_name: # Check if the name matches
|
|
return i # Return the index
|
|
for i, full_name in enumerate(AssignmentData.xyNames): # Loop through the names in the list
|
|
if address_string in full_name: # Check if the name matches
|
|
return i # Return the index
|
|
print(f"Address {address_string} is not in Full List") # Report items not found in the list
|
|
return 0 # Default to Hub if not found
|
|
|
|
def driveNextClosest(self, priority: DerekHashTable) -> datetime.time: # The bread and butter of the algorithm
|
|
# deal with priority
|
|
priorityPkgs = [] # set the packages we currently want to look for
|
|
for cur in self.packages: # loop thorugh looking for priority matches
|
|
if priority.getItem(cur.id):
|
|
priorityPkgs.append(cur) # if a priority matches add it to the list
|
|
if not priorityPkgs: # if no priority matches just use the normal
|
|
for v in self.packages:
|
|
if ("Wrong address listed" not in v.notes or # If the package is marked wrong address
|
|
self.getTimeOfDay() >= datetime.time(10, 30)): # Wait for corrected shipping address
|
|
priorityPkgs.append(v) # add to the list
|
|
|
|
# get the next closest
|
|
shortest_dist = float('inf') # Start with a very long distance to work backwards from
|
|
next_pkg = None # Keep track of the closest pkg
|
|
|
|
if len(priorityPkgs) == 0:
|
|
priorityPkgs.append(DeliveryObject(-1, "HUB", "", "", 0, "10:30 AM", 21),) # Add a blank package in order to get the truck to drive back to HUB
|
|
|
|
current_idx = self.getMatrixIndex(self.current_location) # Get the trucks xyIndex
|
|
for pkg in priorityPkgs: # Loop through the packages
|
|
dest_idx = self.getMatrixIndex(pkg.address) # Get the package xyIndex
|
|
|
|
row = max(current_idx, dest_idx) # Becuase the distance table is a triange
|
|
col = min(current_idx, dest_idx) # Keep on the buttom left side
|
|
dist = AssignmentData.distanceChart[row][col] # Get the distance from the grid
|
|
|
|
if dist < shortest_dist: # If the distance is shorter than the one found
|
|
shortest_dist = dist # Update the distance and package
|
|
next_pkg = pkg
|
|
|
|
# drive
|
|
self.mileage += shortest_dist # Add this shortest distance to the truck
|
|
self.current_location = next_pkg.address # update the location
|
|
self.deliverPkgs() # deliver packages at this location
|
|
if len(self.packages) == 0: # if there is no packages
|
|
self.hasLoad = False # Set the load to empty |