Build your app
This page details the steps for building your routing app with Nextmv. If this is your first Nextmv app, be sure you have completed all of the prerequisites.
Note, Github access is required to get started. Please contact support@nextmv.io to request access.
Write input data as JSON
Input data must be written in [JSON][json] for use with Nextmv (see below for a sample input file from the Dispatch App).
Nextmv's tools are designed to operate directly on business data (in JSON) to produce decisions that are actionable by software systems. This makes decisions more interpretable and easier to test. It also makes integration with data warehouses and business intelligence platforms significantly easier.
{
"vehicles": [
{
"id": "Mvrs-N-Shkrs-1",
"start": { "lon": -96.786455, "lat": 32.82729 },
"end": { "lon": -96.786455, "lat": 32.82729 }
},
{
"id": "Mvrs-N-Shkrs-2",
"start": { "lon": -96.786455, "lat": 32.82729 },
"end": { "lon": -96.786455, "lat": 32.82729 }
}
],
"stops": [
{
"id": "Parent's Basement",
"position": { "lon": -96.827094, "lat": 33.004745 }
},
{
"id": "Van Down By The River",
"position": { "lon": -96.86074, "lat": 33.005741 }
},
{
"id": "McMansion",
"position": { "lon": -96.820915, "lat": 32.969456 }
},
{
"id": "Cozy English Cottage",
"position": { "lon": -96.695259, "lat": 32.921629 }
},
{
"id": "Apartment Above the Bodega",
"position": { "lon": -96.673973, "lat": 32.875507 }
},
{
"id": "18th Floor - Elevator is Broken",
"position": { "lon": -96.682899, "lat": 32.810318 }
},
{
"id": "Still Packing",
"position": { "lon": -96.75642, "lat": 32.66026 }
},
{
"id": "Hasn't Started Packing",
"position": { "lon": -96.87521, "lat": 32.673555 }
},
{
"id": "Shack",
"position": { "lon": -96.888943, "lat": 32.772914 }
},
{
"id": "Mother-In-Law Suite",
"position": { "lon": -97.044811, "lat": 32.862358 }
},
{
"id": "Apartment Above Garage",
"position": { "lon": -97.021465, "lat": 32.933848 }
},
{
"id": "Dilapidated Old House",
"position": { "lon": -96.87521, "lat": 32.84563 }
},
{
"id": "Barn Loft",
"position": { "lon": -96.893749, "lat": 32.649276 }
},
{
"id": "American Hoarder",
"position": { "lon": -96.943874, "lat": 32.653323 }
},
{
"id": "5th Floor Walk Up",
"position": { "lon": -96.846371, "lat": 32.748662 }
},
{
"id": "Family of 9",
"position": { "lon": -96.842251, "lat": 32.735956 }
},
{
"id": "Don't forget the Yacht",
"position": { "lon": -96.840191, "lat": 32.714583 }
},
{
"id": "7 Cats 2 dogs and a horse",
"position": { "lon": -96.808605, "lat": 32.710539 }
},
{
"id": "Triplets",
"position": { "lon": -96.595058, "lat": 32.82486 }
},
{
"id": "Everything is in trash bags",
"position": { "lon": -96.64175, "lat": 32.772914 }
},
{
"id": "Tetris Queen",
"position": { "lon": -96.573772, "lat": 32.94134 }
},
{
"id": "Boathouse",
"position": { "lon": -96.87521, "lat": 32.895804 }
},
{
"id": "House on a Boat",
"position": { "lon": -96.950741, "lat": 32.844476 }
},
{
"id": "Pool House",
"position": { "lon": -96.910915, "lat": 32.939035 }
},
{
"id": "Cabin In The Woods",
"position": { "lon": -96.592999, "lat": 32.745775 }
},
{
"id": "Haunted House",
"position": { "lon": -97.271404, "lat": 32.933272 }
},
{
"id": "House on the Hill",
"position": { "lon": -97.20274, "lat": 32.902722 }
},
{
"id": "Brick and Mortar",
"position": { "lon": -96.990566, "lat": 32.85659 }
},
{
"id": "Stucco",
"position": { "lon": -96.831951, "lat": 32.618629 }
},
{
"id": "Cat Condo",
"position": { "lon": -96.785259, "lat": 32.706495 }
},
{
"id": "French Villa",
"position": { "lon": -96.874523, "lat": 32.706495 }
},
{
"id": "Trailer Park",
"position": { "lon": -96.992626, "lat": 32.800045 }
},
{
"id": "Tiny Home",
"position": { "lon": -97.012539, "lat": 33.019675 }
},
{
"id": "Tree Hollow",
"position": { "lon": -96.961727, "lat": 32.973028 }
},
{
"id": "Fairy Garden",
"position": { "lon": -97.002926, "lat": 32.796005 }
}
]
}
Define a Go struct
Next, you will need to define a Go struct for your data (typically this is
stored in a data.go file as part of your model). The Go struct
for the sample data with vehicles and stops is provided below.
// Input contains Vehicles and Stops.
type input struct {
Vehicles []Vehicle `json:"vehicles"`
Stops []Stops `json:"stops"`
}
Define the state
Next, you need to define a decision or system state to track (typically this is
stored in a state.go file).
// state handles the translation between the current Hop fleet engine fleet.State and
// the app's own state (represented through the app's schema attributes).
type state struct {
model fleet.State
input input
}
You can choose to store any data on your model state (e.g., the final location of the driver, the cost of the route, or the current path as a slice of integers to refer to the locations by index).
Add methods
Add Feasible, Next, and any other methods (e.g., Value) as needed.
The Next method determines how to hop from one state to another. It points to
fleet engine's Next function and updates an app's state.
func (s state) Next() model.Expander {
expander := s.model.Next()
if expander == nil {
return nil
}
expandingFunction := func() model.State {
nextState := expander.Expand()
if nextState == nil {
return nil
}
return state{
model: nextState.(fleet.State),
input: s.input,
}
}
return expand.Lazy(expandingFunction)
}
The Feasible method determines if the current state is a possible solution. It
points to the fleet engine's Feasible function.
func (s state) Feasible() bool {
return s.model.Feasible()
}
The Value method determines the objective's quantity for the current state. It
points to the fleet engine's Value function.
func (s state) Value() int {
return s.model.Value()
}
Go build your app
Finally, run go build (e.g., in cmd/cli for a CLI runner if your project
follows the recommended structure) to create a binary for
your model.
The binary created will read input structures from standard input and write
state structures to standard output. Note, if using the CLI runner, you can
add flags to configure where to read input from and where to write output to,
along with other options (see example below).
./model-cli \
-hop.runner.input.path data/input.json \
-hop.solver.limits.duration 10s \
-hop.runner.output.path data/output.json