Git Product home page Git Product logo

ghostkitchen's Introduction

GhostKitchen


1. Running the Program

This a CLI program created on Mac running Mac OS Catalina Version 10.15.5, using Xcode version 11.6 in Swift. You can run the program two ways. The first way is by opening the .xcworkspace file in the directory and click the play button in the upper left hand corner of Xcode. To see the output of the program open up the debugger of Xcode. The 2nd way is to navigate to the /bin directory and run the executable.

The Simulation class is responsible for containing the information needed to run the kitchen simulation. Orders from orders.json are parsed and placed into the simulation instance along with a configurable orders per second ingestion rate, and a GhostKitchen instance, which is explained in the sections below.

Please note if you are running from the CLI, you need to rebuild the program in Xcode whenever you make any changes.

2. Architecture Overview

The program is split into two main Modules the first is called KitchenModule and the 2nd is called DeliveryModule. KitchenModule is responsible for all things related to cooking, shelving, and monitoring decay of orders. DeliveryModule is responsible for all things related to dispatching couriers, routing them, and delivering orders.

The two modules are composed on what is called a GhostKitchen. The idea behind this was cooking, shelving, and monitoring order health, should have no idea/care about creating delivery schedules, tracking order status, and delivering orders, and vice versa. Having said that, A DeliveryModule needs to know when to dispatch a courier, and a KitchenModule needs to know when an order is going to be picked up. The GhostKitchen handles these conversations between the modules by acting as a listener for significant events in the KitchenModule and DeliveryModule.

Here is an example of how that works

extension GhostKitchen: KitchenModuleDelegate {
	func kitchenModule(kitchenModule: KitchenModule,
				receivedOrders: [Order]) {
		self.deliveryModule.courierDispatcher.dispatchCouriers(forOrders: receivedOrders)
	}
}

Here, the GhostKitchen has a callback from the KitchenModule when it received orders. Once it receives orders, the GhostKitchen immediately tells the DeliveryModules dispatcher to dispatch couriers to pickup the order.

Currently, the KitchenModule has these callbacks that the GhostKitchen listens for.

protocol KitchenModuleDelegate {
	func kitchenModule(kitchenModule: KitchenModule,
				 receivedOrders: [Order])
	
	func kitchenModule(kitchenModule: KitchenModule,
						cooked: [Order])
	
  	func kitchenModule(kitchenModule: KitchenModule,
							removed: Order,
							fromShelf: Shelf,
							reason: ShelveOrderDistributorRemovalReason)
}

The DeliveryModules has these callbacks that the GhostKitchen listens for.

protocol DeliveryModuleDelegate {
	func deliveryModule(deliveryModule: DeliveryModule,
						courier: Courier,
						arrivedForOrder:Order,
						onRoute:Route)

	func deliveryModule(deliveryModule: DeliveryModule,
						courier: Courier,
						deliveredOrder: Order)
	
	func deliveryModule(deliveryModule: DeliveryModule,
						routed: Courier,
						forOrder: Order)
}

Logging takes place when an order is received, cooked, shelved, courier routed, order removed (for either pickup, overflow, or decay), courier arrived, and courier dropoff.

Shelve contents are printed when an order is received, shelved, removed (for any reason), and when an order is delivered. I chose not to print shelve contents on courier routed, and courier arrived, due to the verbosity of printing since when a courier is routed the order is cooked at the same time which prints the shelves, then when a courier arrived the contents get printed when an order is removed which is instant for the sake of this simualation.

3. Models Deep Dive

The program has 5 model objects. Order, Courier, Schedule, Task, and Shelf. Order and Shelf were provided in the directions, so I will explain the 3 I created.

Courier, Schedule, and Task were created to model the delivery flow. A Courier gets dispatch to an Order. When a Courier gets dispatched, a Schedule is created for them. A Schedule contains an array of Tasks and each Task has an orderId, taskType (pickup or dropoff) and duration. This means that for 1 order a schedule will have two tasks associated with it, pick up and dropoff.

4. KitchenModule Deep Dive

The KitchenModule is composed of an OrderCooker and a ShelveOrderDistributor. An OrderCooker is responsible for taking in orders to cook, and notifying the consumer when they are finished. A ShelveOrderDistributor is responsible for shelving orders in the best possible place, removing them when a courier picks up them, removing them when there is overflow, or removing them when an order decays.

The ShelveOrderDistributor is composed of an OrderDecayMonitor that monitors the orders on its parents shelves and notifies the parent (in this case the ShelveOrderDistributor) when an order has gone bad. See Order Decay Extra Credit for more information.

These two modules communicate with each other by being composed on the KitchenModule which listens to significant events from the ShelveOrderDistributor and the OrderCooker.

5. DeliveryModule Deep Dive

The DeliveryModule is composed of a CourierRouter and a CourierDispatcher. A CourierDispatcher is responsible for dispatching a courier to pick up an Order. It does this by creating a Schedule for them to follow.

Please see Models Deep Dive for how a Schedule works. A CourierRouter is responsible for routing a courier to a pickup and dropoff. These two modules communicate with each other by being composed on the DeliveryModule which listens to significant events from the CourierDispatcher and the CourierRouter.

6. Order Decay Extra Credit

Order Decay Logic is encapsulated inside the OrderDecayMonitor. The OrderDecayMonitors job is to essentially monitor its parents shelves for decayed orders. If it detects a decayed order, it notifiys its delegate. It does not perform any removals of the orders itself. It is just a monitor and tells whoever its delegate is that an order decayed and does not care what its delegate does when that happens. I composed the OrderDecayMonitor on the ShelveOrderDistributor.

Here is a look at the decay monitors datasource and delegate.

// MARK: OrderDecayMonitorDelegate

protocol OrderDecayMonitorDelegate {
	
    /**
     A delegate callback that lets the consumer know when an order has decayed.

     - Parameters:
        - monitor: The monitor that detected the decay
        - detectedDecayedOrder: The decayed order
     */
	func orderDecayMonitor(monitor: OrderDecayMonitor,
						   detectedDecayedOrder: Order)
	
}

// MARK: OrderDecayMonitorDataSource

protocol OrderDecayMonitorDataSource {
	
    /**
     The shelves that the OrderDecayMonitor will monitor for decay.
     */
	func monitoringShelves() -> [Shelf]
}

As you can see from the OrderDecayMonitorDataSource it monitors its datasources shelves, so whenever its datasource removes an object from its shelves, the monitor will automatically be updated.

An orders decay will be nil until set the order decay gets updated by the callback from OrderDecayMonitor in the ShelveOrderDistributor. Decaying starts happening a second after an item is placed on a shelf, so note that while looking at logs.

7. Testing Suite

Tests can be found under GhostKitchenTests. CMD + U is a shortcut for running tests. Current Coverage is at 89.3%

8. FAQ

  • How do I change the ingestion rate? You can change the ingestion rate by going into main.swift and change the ingestion parameter on the simulation.

  • How can I change the simulation orders json? If you are running the program from Xcode You can modify the json by simply opening the orders.json file in the project and modifying it to however you please.. If you are running from the command line, modify the orders.json file in the /bin

ghostkitchen's People

Contributors

lukegeiger avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.