import time, datetime, math from copy import deepcopy from enum import Enum def now(): return datetime.datetime.now().timestamp() class DroneState(Enum): Grounded = 0 InTheAir = 1 WaitingForTakeOff = 2 class StopType(Enum): City = 0 Factory = 1 class Position: def __init__(self, x, y): self.x = x self.y = y def __str__(self): return(f"[{self.x}, {self.y}]") class Drone: def __init__(self, name, position, route, rate, capacity): self.__name = name self.__position = position self.__route = route self.__rate = rate # per second self.__capacity = capacity self.__cargo = {} self.__state = DroneState.Grounded self.__prevousStop = -1 self.__nextStop = 0 self.__lastDeparture = 0 self.__lastDeparturePosition = deepcopy(position) def set_position(self, position): self.__position = position # returns how much was accepted from the load def load_cargo(self, product, quantity): cargoTotal = self.get_cargo_total() if not product in self.__cargo: self.__cargo[product] = 0 spaceRemaining = (self.__capacity - cargoTotal) acceptedQuantity = quantity # if the quantity is more than space remaining then just return the space remaining if(quantity > spaceRemaining): acceptedQuantity = spaceRemaining self.__cargo[product] += acceptedQuantity return acceptedQuantity def get_cargo_total(self): total = 0 for key in self.__cargo: total += self.__cargo[key] return total def takeOff(self): self.__state = DroneState.InTheAir self.__lastDeparture = now() self.__lastDeparturePosition = deepcopy(self.__position) positionFound = False for idx, stop in enumerate(self.__route.get_stops()): stopPosition = stop.get_position() if (stopPosition.x == self.__position.x) and (stopPosition.y == self.__position.y): positionFound = True self.__prevousStop = idx # if the next stop is out of range, loop back home if(idx + 1 == len(self.__route.get_stops())): self.__nextStop = 0 else: self.__nextStop = idx + 1 if(not positionFound): self.__prevousStop = -1 self.__nextStop = 0 print(f"{self.__name}: Taking off from: {self.__position}") else: print(f"{self.__name}: Taking off from: {self.__route.get_stops()[self.__prevousStop]}") def cargo_report(self): str = "" for product in self.__cargo: str += f"{product}: {self.__cargo[product]}" return def update(self): state = DroneState.Grounded.name position = f"Position: {self.__position}" prevousStop = self.__route.get_stops()[self.__prevousStop] nextStop = self.__route.get_stops()[self.__nextStop] # print cargo levels print(f"{self.__name}: On Board {self.__cargo}") # are we at a unknown location? if(self.__prevousStop == -1): location = self.__lastDeparturePosition else: location = prevousStop # is the drone in the air? if(self.__state == DroneState.Grounded): print(f"{self.__name}: {state}: {location}") elif(self.__state == DroneState.WaitingForTakeOff): currentStop = prevousStop print(f"{self.__name}: Waiting For Take Off: {location}") # load cargo/receive_supplies if at a factory if(currentStop.get_stop_type() == StopType.Factory): print(f"{self.__name}: Loading Inventory") products = currentStop.get_output_quantity() for key in products: acceptedQuantity = self.load_cargo(key, products[key]) # adjust inventory by accepted quantity currentStop.update_inventory(-acceptedQuantity) currentStop.receive_supplies(self.__cargo) if(self.__nextTakeOff <= now()): self.takeOff() else: print(f"{self.__name}: Traveling Between: {location} and {nextStop}") # time in the air since take off timeDifference = math.floor(now() - self.__lastDeparture) x2 = float(nextStop.get_position().x) y2 = float(nextStop.get_position().y) if(self.__prevousStop == -1): x1 = float(self.__lastDeparturePosition.x) y1 = float(self.__lastDeparturePosition.y) else: x1 = prevousStop.get_position().x y1 = prevousStop.get_position().y #print(f"{self.__name}: {x1},{y1} -> {x2},{y2}") # total distance: d=√((x2 – x1)² + (y2 – y1)²) totalDistance = (math.sqrt(abs(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1))))) # d = rt distanceTraveled = self.__rate * (now() - self.__lastDeparture) #print(f"{self.__name}: Distance Traveled: {distanceTraveled}, Total Distance: {totalDistance}") # slope m = (y2 - y1) / (x2 - x1) if(x2 == x1): m = 0 else: m = (y2 - y1) / (x2 - x1) travelingEast = x2 > x1 travelingNorth = y2 > y1 # if traveled the distance, ground the drone and update stops if(distanceTraveled >= totalDistance): # force position self.__position.x = nextStop.get_position().x self.__position.y = nextStop.get_position().y self.__state = DroneState.WaitingForTakeOff self.__nextTakeOff = now() + 5 self.__prevousStop = self.__nextStop self.__nextStop += 1 # if next stop out of range, sent back to first stop if(self.__nextStop >= len(self.__route.get_stops())): self.__nextStop = 0 else: # what percentage of the journey has passed? per = (distanceTraveled/totalDistance) # compute X if(travelingEast): x = x1 + ((x2 - x1) * per) else: x = x1 - ((x1 - x2) * per) # compute y if(m > 0): # if there is a slope #(y – y1) = m(x – x1) y = (m * (x - x1)) + y1 else: if(travelingNorth): y = y1 + ((y2 - y1) * per) else: y = y1 - ((y1 - y2) * per) # if no slope, either x or y is constant if(x1 == x2): x = x1 if(y1 == y2): y = y1 self.__position.x = x self.__position.y = y print(f"{self.__name} {position}") def get_state(self): return self.__state class Route: def __init__(self, name, stops): self.__name = name self.__stops = stops def get_stops(self): return self.__stops class Product(): def __init__(self, name): self.__name = name def __str__(self): return f"{self.__name}" def get_name(self): return self.__name class Stop: def __init__(self, stopType, name, position): self.__stopType = stopType self.__name = name self.__position = position def __str__(self): return f"{self.__name} {self.__position}" def get_position(self): return self.__position def get_name(self): return self.__name def get_stop_type(self): return self.__stopType class Factory(Stop): def __init__(self, name, position, product, buildRate, acceptedSupplies): super().__init__(StopType.Factory, name, position) #product name self.__product = product #build rate (units per second) self.__buildRate = buildRate #units ready to ship self.__inventory = {product: 0} #last time inventory was updated self.__lastCheck = 0 #acceptedSupplies = products accepted by this factory self.__acceptedSupplies = acceptedSupplies # returns a dictionary of the product and inventory level def get_output_quantity(self): # only return finished goods return {self.__product: math.floor(self.__inventory[self.__product])} # adds to inventory level def update_inventory(self, quantity): self.__inventory[self.__product] += quantity def update_supply_inventory(self, product, quantity): if product not in self.__inventory: self.__inventory[product] = 0 self.__inventory[product] += quantity def receive_supplies(self, supplies): for supply in supplies: if supply in self.__acceptedSupplies: self.update_supply_inventory(supply, supplies[supply]) def inventory_report(self): str = f"{self.get_name()}: " for product in self.__inventory: str += f"{product}: {self.__inventory[product]} " return str # runs on timer tick def update(self): now = datetime.datetime.now().timestamp() if(self.__lastCheck > 0): timeDifference = (now - self.__lastCheck) self.update_inventory(timeDifference * self.__buildRate) print(self.inventory_report()) self.__lastCheck = datetime.datetime.now().timestamp() chicago = Stop(StopType.City, "Chicago", Position(1, 45)) miami = Stop(StopType.City, "Miami", Position(50, 1)) newYork = Stop(StopType.City, "New York", Position(50, 50)) widget = Product("Widget") gizmo = Product("Gizmo") widgetFactory = Factory("Widget Inc", Position(50, 25), widget, .25, []) gizmoSupplies = [widget] gizmoFactory = Factory("Gizmo Inc", Position(50, 30), gizmo, .25, gizmoSupplies) factories = [widgetFactory, gizmoFactory] route = Route(name="Express Route", stops=(widgetFactory, gizmoFactory)) routeRev = Route(name="Express Route Reverse", stops=(miami, newYork, chicago, widgetFactory)) alphaDrone = Drone("Alpha", Position(25, 25), route, 1, 10) betaDrone = Drone("Beta", Position(35, 25), routeRev, 5, 10) drones = [alphaDrone] while(True): for drone in drones: if(drone.get_state() == DroneState.Grounded): drone.takeOff() else: drone.update() for factory in factories: factory.update() pass time.sleep(1)