• Cadence
  • Dev Rel
  • Apache Kafka
Spinning Your Drones with Cadence – Introduction – Part 3

Let’s build a new and more complex Cadence application for Drone Deliveries! This is the first part of a multi-part Cadence Drone series and introduces the Drone Delivery problem, the main Drone Cadence Workflow, and an appendix on Drone movement.

1. The Sky Is Full of Drones (And Ravens)

(Source: Shutterstock)

My recent New Year’s holiday reading was Neal Stephenson’s “Termination Shock”,  imagining a time in the near future in which drones are ubiquitous, and curiously features trained drone-destroying birds of prey. Fact is often stranger than fiction, as this reminded me about the trial drone delivery service near the part of Canberra where I live, and which had recently been in the news due to Raven attacks. Unfortunately, when I checked it turns out that they don’t deliver to my suburb yet, even though I have multiple perfect drone delivery landing locations nearby. Oh well, fiction will have to suffice for the time being.

So, for my next Cadence experiment, I decided to build a Drone Delivery Service (simulated, even though I do have several generations worth of discarded drones cluttering up my garage!) It will be modeled on the real local trial, which works by allowing customers to order small items from selected participating shops (e.g. medicines, coffee, food, small parts, etc.). There is a single Drone Base (similar to, but probably more boring than an Evil Villain’s lair, Tracy Island from Thunderbirds “are go”, or the Bees Drone Congregation Area) where all the drones hang out when they aren’t out on a delivery.

Bee Drone Congregation Area (DCA) (Source: Shutterstock)

The base is where they recharge, and when an order is ready to go, they fly from the base to the shop, pick up the order, then fly to the customer’s location and deliver the order. They then return to base and the process repeats. The drones are autonomous, so there can be lots of them. But there are limits to the weight of goods, the times of operation, where they can and can’t fly (e.g. exclusion zones), many potential failure modes (e.g. failure to reach destinations or deliver orders, crashing, etc.), and maximum distances the drones can operate over in a single charge with sufficient safety margin to return to base (particularly if they have to avoid attacking ravens or get blown off course, etc.). The only factor I decided to take into consideration was the distance and time of travel for the time being.

2. Drone Delivery Application: Cadence Workflow Design

The High-Level Drone Workflow (Source: Shutterstock—composite)

The High-Level Drone Workflow (Source: Shutterstock—composite)

I wanted to keep things simple, but also interesting, for my Drone Delivery Demo. So I decided to implement everything that was stateful and could have actions or tasks as Cadence workflows. There are two Workflow types that interact.

Each Drone is modeled as a workflow, which transitions through various states depending on whether it’s ready at the base, waiting for an order, flying to collect an order, picking it up, flying to deliver it, delivering it, and then returning to base and charging.  After each of these delivery cycles, the Drone Workflow starts a new instance (with the same Workflow Id, but a different Run Id), and is ready for the next delivery.

I also decided to model Orders as Cadence Workflow. This is because Orders also have a state and transition from one state to the next (e.g. order creation, ready for pick up, picked up by Drone, on way, delivered, order complete). They may also have actions. For example, the Order Workflow is actually responsible for generating random Order pickup and Delivery locations that are within Drone flying range of the base location, and indicating when the Order is ready for pickup. We’ll look at the Order Workflow in more detail in the next blog.

Here are the top-level Cadence Drone Workflow steps. One Drone Workflow instance is created initially per Drone. The workflow models a single complete delivery of one Order, including drone recharging, and then starts a new instance that is fully charged and ready for the next delivery. Note that this code is just the @WorkflowMethod (entry point) method of the Drone workflow implementation and is incomplete by itself—it uses some private helper functions for some steps and needs the Workflow interface and activities to run. The complete code is available here.

3. Cadence Use Case: Drone Delivery From A to B

Drones Delivering From A to B (Source: Shutterstock)

Drones Delivering From A to B (Source: Shutterstock)

The above code and this picture makes Drone deliveries from one location to another look easy. In practice, it’s a bit more complex, and motivates my choice of using Cadence Activities for some of the workflow steps.

Location is key for Drone deliveries. I’ve chosen to use the latitude and longitude coordinates (in decimal), with a resolution of 1m. Each drone has a “GPS” that keeps track of the drone’s location, and the Workflow instance for the drone has location state. Drones start at, and (hopefully) return to, the base location. Orders and their corresponding workflow instances, also have location state, including the location of the order pickup, the location where the order is to be delivered to, and finally the actual location of the order, which is updated as the Order is transported by the drone. This is important as the Drone delivery service and shops want to know where the Order is at all times, and also so the customer can track when their delivery is about to arrive so they can collect it from the drone (and save their delivery from angry ravens). 

Here’s a map of an example of complete Drone delivery:

Before embarking on a treasure hunt, the pirates order lunch from the local village! (Source: Shutterstock)

Before embarking on a treasure hunt, the pirates order lunch from the local village! (Source: Shutterstock)

But how does a drone move from one location to another? I wrote a simple Drone Maths package to help out with calculations. If you are interested in the details of how we compute drone movement from one location to another, see the Appendix below.  The function DroneMaths.nextPosition(start, end, speed, time) computes the next position of the drone given the start location, the end location, and the speed and flight time, and it’s used in the nextLeg()  activity. 

4. Cadence Workflow Activities

The nextLeg() activity is responsible for simulating the movement of the drone for each leg of the delivery flight and stopping when it arrives at the destination. It updates the Drone location and charge, and optionally the Order location as it “flies”. The activity returns when the drone has arrived. We use a Cadence Activity for this because it potentially runs for a long time, is computationally demanding due to the use of the Drone Maths, so should run in a separate thread to the Drone Workflow itself, and it could potentially fail (as could a real drone, i.e. a non-simulated, real flying drone interacts with the real world, and is also a good example of a non-deterministic activity),  in which case we will eventually want to ensure that it restarts and continues the drone movement from where it left of, but we’ll leave that aspect until the next blog when we delve into exception handling in more detail.

You will notice that I’ve used the general purpose Thread.sleep() call in the activity code. This is actually fine, as a Cadence Activity (unlike a Cadence Workflow), can use arbitrary code. However, I did discover one limitation with Activities. It turns out that Activity method arguments and return values must be serializable to a byte array using the provide DataConverer (the default implementation uses a JSON serializer). This is why I had to pass a String OrderID to the method, and construct a new OrderWorkflow instance with it, rather than just pass it a OrderWorkflow (which wasn’t serializable).

Finally, there is one Drone Worklfow step, step1_GetOrder(), that’s critical to progress the Drone workflow. This step waits until an Order is ready for delivery, and sets the Order ID and an OrderWorkflow instance in the main workflow. This helper method is actually a wrapper for another Activity, which blocks until an Order is ready for pickup by a Drone, and guarantees that each ready Order is picked up by exactly one drone. How does this work? It’s actually another example of a Cadence+Kafka integration pattern, and we’ll explain it further in the next blog, and some other Kafka+Cadence patterns (including starting a Cadence Workflow with Kafka, which is how we actually create Order Workflows). In the next blog we’ll also explore the complete solution, including the Order Workflows, and other Cadence features, including queries, retries, heartbeats, continue as new, and side-effects.

P.S. Perhaps the local Drone Delivery company heard about my project? I just received a pamphlet in the mailbox (yes, very old school, but maybe drone delivered?) advertising the start of the service to my suburb—bring on the drone-delivered stuff! (I also discovered that their drones are much faster than mine, I will have to increase the average speed to 100km/h to keep up).

5. Appendix—Drone Movement: Location, Distance, Bearing, Speed, and Charge!

How does a drone move from one location to another? I wrote a simple Drone Maths package to help out with calculations (most of the formulas are covered here). First, we need a function to compute distance (in km) between two lat/lon decimal locations (this also takes into account the curvature of the earth, but for the short distances we’re dealing with the impact of curvature is insignificant—unless the drones are flying very high of course). This is to ensure that the complete delivery (and return) is within the drone’s maximum range capability. 

But how do we navigate from one location to another? It turns out that this is just old-time ship-style navigation. For this, we need to know the bearing (in degrees) from one location to another (zero degrees is due North, 90 degrees is due East, 180 degrees is due South, and 270 degrees is due West).

Ships can be navigated around the world with just a map, compass, and dividers. (Source: Shutterstock)

Ships can be navigated around the world with just a map, compass, and dividers. (Source: Shutterstock)

And finally, to work out the next “waypoint” and enable the drone to move on the shortest direct path from one location to another (“as the crow flies”, or “in a beeline”, which is appropriate for drones—we assume there are no obstacles or exclusion zones in the way, including ravens), we have a function to compute the next drone location, nextPosition(). This finds the bearing from the current location to the intended destination location (Order location, delivery location, or base location depending on what state the drone is in), and given the speed (we assume an average drone speed of 20km/hr over the entire journey), calculates the distance traveled in the next time interval (which is configurable, and can be scaled for faster than real-time simulations). It then calls another function to compute the location given the current location, distance covered, and bearing.

If we’ve reached the destination already, we can stop flying and perform the next action (e.g. pickup order, drop order, descend to base). Currently, we assume that each flight path is successful, but for fun, various exceptions (wind, ravens, crashes, etc.) could be introduced with the exceptions handled appropriately (depending on the drone state as well, e.g. if the drone can’t pick up a delivery for some reason, the Order should be rescheduled, perhaps using a priority queue, so that the next available drone is allocated the undelivered Order before accepting other orders).

The nextLeg() activity (see above) is responsible for computing multiple drone movements for each leg of the delivery, but importantly it also sends location updates to the drone and the associated order using Cadence signals, which we saw working in the previous blog. The Drone workflow itself also signals the Order workflow for state changes (e.g. orderWorkflow.signalOrder(“droneHasOrder”)).

The next drone location is computed from the current location, bearing, speed, and distance (Source: Shutterstock)

The next drone location is computed from the current location, bearing, speed, and distance (Source: Shutterstock)

Drones are electrical so consume power as they fly and hover, etc. For each incremental distance the drone flies, we reduce the battery charge using a helper function updateCharge(time) which computes the amount of charge used based on the supplied flying time.

You don’t want your drones to run out of charge before they return to base. (Source: Shutterstock)

You don’t want your drones to run out of charge before they return to base. (Source: Shutterstock)

Learn more about Cadence and the benefits it can provide to your organization!

Download White Paper

Follow the series: Spinning Your Drones With Cadence

Part 1: Spinning Your Workflows With Cadence!

Part 2: Spinning Apache Kafka® Microservices With Cadence Workflows

Part 3: Spinning your drones with Cadence

Part 4: Architecture, Order and Delivery Workflows

Part 5: Integration Patterns and New Cadence Features

Part 6: How Many Drones Can We Fly?