public static void makeDistanceTimeCapDims(Ivr7.Model m)
    {
        m.Dimensions = new Ivr7.DimensionConfiguration();

        m.Dimensions.timeConfig = new Ivr7.InternalDimension
        {
            Id = "time",
            measurementUnit = Ivr7.InternalDimension.eMeasurementUnit.Minutes,
            slackMax        = (float)1e6,
            tardyMax        = 0
        };
        m.Dimensions.distanceConfig = new Ivr7.InternalDimension
        {
            Id = "distance",
            measurementUnit = Ivr7.InternalDimension.eMeasurementUnit.Kilometres,
            slackMax        = 0,
            tardyMax        = 0
        };

        m.Dimensions.capacityDimensions.Add(
            new Ivr7.CapacityDimension
        {
            Id       = "capacity",
            Units    = "kg",
            slackMax = 0,
            tardyMax = 0
        }
            );
    }
    public static void makeJobTimeCap(Ivr7.Model m, List <dataRow> d, List <int> srcs, List <int> dests)
    {
        m.Jobs.Clear();
        if (srcs.Count != dests.Count)
        {
            throw new System.Exception("Expected srcs.Count == dests.Count");
        }
        for (int i = 0; i < srcs.Count; i++)
        {
            int      si = srcs[i];
            int      di = dests[i];
            Ivr7.Job j  = new Ivr7.Job();
            j.Id                    = "job_" + d[di].id;
            j.pickupTask            = new Ivr7.Job.Task();
            j.pickupTask.taskId     = "pickup_" + d[di].id;
            j.pickupTask.locationId = d[si].id;

            j.pickupTask.Attributes.Add(
                new Ivr7.Job.Task.Attribute
            {
                dimensionId = "time",
                Quantity    = d[di].pickupTime
            });

            j.pickupTask.Attributes.Add(
                new Ivr7.Job.Task.Attribute
            {
                dimensionId = "capacity",
                Quantity    = d[di].quanity
            }
                );

            j.dropoffTask            = new Ivr7.Job.Task();
            j.dropoffTask.taskId     = "dropoff_" + d[di].id;
            j.dropoffTask.locationId = d[di].id;
            // careful here, in C# the object is passed by ref.
            // so instantiate a new set of attributes, don't reuse the object's created
            // prior to this.
            j.dropoffTask.Attributes.Add(
                new Ivr7.Job.Task.Attribute
            {
                dimensionId = "time",
                Quantity    = d[di].dropoffTime
            });

            j.dropoffTask.Attributes.Add(
                new Ivr7.Job.Task.Attribute
            {
                dimensionId = "capacity",
                Quantity    = -d[di].quanity
            }
                );
            j.Penalty = 10000;
            m.Jobs.Add(j);
        }
    }
 public static void makeLocations(Ivr7.Model m, List <dataRow> d)
 {
     m.Locations.Clear();
     foreach (var l in d)
     {
         m.Locations.Add(new Ivr7.Location
         {
             Id      = l.id,
             Geocode = new Ivr7.Geocode
             {
                 Longitude = l.X,
                 Latitude  = l.Y
             }
         });
     }
 }
    public void Run()
    {
        var api = new ApiHelper <Ivr7.SolveRequest, Ivr7.SolutionResponse>("ivr7-kt461v8eoaif", configFile);
        // so here we're going to build the model

        var m = new Ivr7.Model(); // initialise the model container

        // we're going to reuse the helpers described in the ivr7basic example. Please see that for an initial reference.
        // We want "Open Routing" which basically means that if a vehicle finishes it's day at any node,
        // it is then not supposed to cost the return trip home. So as a start, we need to separate the
        // depot (Guiness Storehouse) from the vehicle-home location by name - it's okay if it's at the
        // same point (long/lat), but we're going to separate the identifier so we can control the
        // distance/time matrix nicely.

        ivr7helper.makeDistanceTimeCapDims(m); // adds distance, time & capacity

        ivr7helper.makeLocations(m, data);     // adds all the locations to the model
        // we're going to add an exta location; the "vehicle-site"
        m.Locations.Add(new Ivr7.Location
        {
            Id      = "vehicle-site",
            Geocode = m.Locations[0].Geocode // use the geocode of the Guiness storehouse.
        });

        ivr7helper.makeJobTimeCap(m, data, ivr7helper.Rep(0, data.Count - 1), ivr7helper.Seq(1, data.Count));
        m.vehicleCostClasses.Add(ivr7helper.makeVccSimple("vcc1", 1000, 0.01f, 0.01f, 0.01f, 1, 3));
        m.vehicleClasses.Add(ivr7helper.makeVcSimple("vc1", 1, 1, 1, 1));
        for (int i = 0; i < 4; i++)
        {
            m.Vehicles.Add(ivr7helper.makeVehicleCap("vehicle_" + i, // unique id for the vehicle.
                                                     "vc1",          // the vehicle class
                                                     "vcc1",         // the vehicle cost class
                                                     2000,           // the capacity of the vehicle
                                                     "vehicle-site", // start location for the vehicle //NOTE the change here.
                                                     "vehicle-site", // end location for the vehicle
                                                     7 * 60,         // start time: 7 AM
                                                     18 * 60         // end time: 6 PM
                                                     ));
        }
        // so we've created vehicles which need to start/end at "vehicle-site". Now we can make
        // the last change which is to override the distance between locations and the "vehicle-site".
        // we're only going to modify the distances FROM locations TO "vehicle-site". If you wanted to
        // do complete line-haul outsourcing-style modelling, you could also do this for "vehicle-site"
        // TO all alocations. For now, we'll just demonstrate the open routing case.

        // you have two ways of doing this. Upload it via the data-api, or upload it as part of the model.
        bool dataUpload = true;

        // it's nice to illustrate this if you've enabled the services on your key
        // but you can set this to false to get a feel for the other code path if needed.

        if (dataUpload)
        {
            var ts = new IVRData.TransitSet();
            for (int i = 0; i < m.Locations.Count; i++)
            {
                ts.Transits.Add(new IVRData.TransitSet.TransitValue
                {
                    fromId = m.Locations[i].Id,
                    toId   = "vehicle-site",
                    Value  = 0
                });
            }

            var datamodel = new IVRData.CachedTransitSet
            {
                transitSet = ts
            };
            var data_api       = new ApiHelper <IVRData.CachedTransitSet, object>("ivrdata-o43e0dvs78zq", configFile);
            var transitModelID = data_api.Post(datamodel);
            // now create the additional transit generators and link them to the data that has been uploaded
            var tgen_d = new Ivr7.TransitGenerator
            {
                Id        = "custom_distance",
                requestId = transitModelID // Note, we're telling the API where to find the Transit-set data.
            };
            var tgen_t = new Ivr7.TransitGenerator
            {
                Id        = "custom_time",
                requestId = transitModelID // we can use the same one here as before, why? because it's all zero :-)
                                           // so we could upload another column of zeros, but there isn't much point.
            };
            m.transitGenerators.Add(tgen_d);
            m.transitGenerators.Add(tgen_t);
        }
        else
        {
            // embed the zero elements in the matrix in the payload directly (rather than through a data-upload)
            var ts = new Ivr7.TransitSet();
            for (int i = 0; i < m.Locations.Count; i++)
            {
                ts.Transits.Add(new Ivr7.TransitSet.TransitValue
                {
                    fromId = m.Locations[i].Id,
                    toId   = "vehicle-site",
                    Value  = 0
                });
            }
            var tgen_d = new Ivr7.TransitGenerator
            {
                Id         = "custom_distance",
                transitSet = ts // or we're explicily providing all the data.
            };
            var tgen_t = new Ivr7.TransitGenerator
            {
                Id         = "custom_time",
                transitSet = ts // or we're explicily providing all the data.
            };
            m.transitGenerators.Add(tgen_d);
            m.transitGenerators.Add(tgen_t);
        }

        // now the last step, we need to tell the vehicles that they should use these
        // transit generators. Note. we're appending the transit generators here, so keeping
        // the roadnetwork distance/time in the list (the order of the list IS important when
        // layering matricies). There are 4 attributes in the vehicle-class list now.
        m.vehicleClasses[0].Attributes.Add(new Ivr7.VehicleClass.Attribute
        {
            dimensionId        = "time",
            transitGeneratorId = "custom_time",
            transitCoef        = 1.0f,
            locationCoef       = 1.0f,
            taskCoef           = 1.0f
        });
        m.vehicleClasses[0].Attributes.Add(new Ivr7.VehicleClass.Attribute
        {
            dimensionId        = "distance",
            transitGeneratorId = "custom_distance",
            transitCoef        = 1.0f,
        });
        // so now we have a roadnetwork distance + time generator
        // followed by a custom time and custom distance generator.
        // the api will execute the transit generators in the order they appear and will
        // override previous values with new values (if they exist). So in this case, it will
        // build a transit matrix using the road network, then overlay the custom matrix which
        // was uploaded with our data.


        var sr = new Ivr7.SolveRequest();

        sr.Model     = m;                                    // could have instantiated the solve request up-front if we'd wanted to
        sr.solveType = Ivr7.SolveRequest.SolveType.Optimise; // Optimise the solve request.

        // now it's just sending the model to the api
        string requestId = api.Post(sr); // send the model to the api

        Solution = api.Get(requestId);   // get the response (which it typed, so that's cool)
        ivr7helper.printSolution(Solution);

        // lets confirm that the distances and times between the last stop and the vehicle-site are indeed zero.
        Dictionary <int, string> stopToLocation = new Dictionary <int, string>();

        foreach (var r in Solution.Routes)
        {
            foreach (var s in r.Stops)
            {
                stopToLocation.Add(s.Id, s.locationId);
            }

            foreach (var e in r.interStops)
            {
                if (stopToLocation[e.fromStopId] != "vehicle-site" &&
                    stopToLocation[e.toStopId] == "vehicle-site")
                {
                    foreach (var a in e.Attributes)
                    {
                        if (a.dimId == "time" || a.dimId == "distance")
                        {
                            if (a.endValue - a.startValue != 0)
                            {
                                throw new Exception("distance and/or time is supposed to be zero!");
                            }
                        }
                    }
                }
            }
        }
        // for visualisations see the R/python notebook for plots on the same example.
        // You'll see in the visuals that the stops that are farthest from the depot are typically
        // selected to be the route (because it results in the largest saving).

        return;
    }
    public void Run()
    {
        var api = new ApiHelper <Ivr7.SolveRequest, Ivr7.SolutionResponse>("ivr7-kt461v8eoaif", configFile);
        // so here we're going to build the model

        // create a solve request

        var m = new Ivr7.Model(); // initialise the model container

        // we're going to reuse the helpers described in the ivr7basic example. Please see that for a reference.
        ivr7helper.makeDistanceTimeCapDims(m); // adds distance, time & capacity
        ivr7helper.makeLocations(m, data);     // adds all the locations to the model
        ivr7helper.makeJobTimeCap(m, data, ivr7helper.Rep(0, data.Count - 1), ivr7helper.Seq(1, data.Count));
        m.vehicleCostClasses.Add(ivr7helper.makeVccSimple("vcc1", 1000, 0.01f, 0.01f, 0.01f, 1, 3));
        m.vehicleClasses.Add(ivr7helper.makeVcSimple("vc1", 1, 1, 1, 1));
        for (int i = 0; i < 4; i++)
        {
            m.Vehicles.Add(ivr7helper.makeVehicleCap("vehicle_" + i, // unique id for the vehicle.
                                                     "vc1",          // the vehicle class
                                                     "vcc1",         // the vehicle cost class
                                                     2000,           // the capacity of the vehicle
                                                     data[0].id,     // start location for the vehicle
                                                     data[0].id,     // end location for the vehicle
                                                     7 * 60,         // start time: 7 AM
                                                     18 * 60         // end time: 6 PM
                                                     ));
        }

        // okay, so that's a basic model. Lets now use the objects, but submit them to the api
        // through a different mechanism.
        var data_api = new ApiHelper <IVRData.CachedModel, object>("ivrdata-o43e0dvs78zq", configFile);
        // so the data api allows us to push just the data, without anything else.
        var dataModel = new IVRData.CachedModel();

        dataModel.Model = ApiHelper <object, object> .SerialiseObject <Ivr7.Model>(m);

        // epic: we just saved our model as a byte stream into this data payload.
        string modelID = data_api.Post(dataModel);

        var sr = new Ivr7.SolveRequest();                    // we can now make a solve request which references the model we've uploaded

        sr.modelID   = modelID;                              // tell the solve request to use the model we uploaded.
        sr.solveType = Ivr7.SolveRequest.SolveType.Optimise; // Optimise the solve request.

        // now it's just sending the model to the api
        string requestId = api.Post(sr); // send the model to the api

        Solution = api.Get(requestId);   // get the response (which it typed, so that's cool)
        ivr7helper.printSolution(Solution);
        // this also means that because we have a model which is versioned separately from the
        // solve request, we can use the solve request with the task-sequence and have that apply
        // to a model. So lets extract the task sequence from the solved model.


        foreach (var r in Solution.Routes)
        {
            List <string> tasklist = new List <string>();
            for (int i = 1; i < r.Stops.Count - 1; i++)
            {
                tasklist.Add(r.Stops[i].taskId);
            }
            if (tasklist.Count > 0)
            {
                var ts = new Ivr7.TaskSequence {
                    vehicleId = r.vehicleId
                };
                foreach (var t in tasklist)
                {
                    ts.taskIds.Add(t);
                }
                sr.Routes.Add(ts); //NOTE: We're adding the tasks to the solve request, not the model - the task sequence will be
                                   // applied to the model we've referenced in this example
            }
        }

        sr.solveType = Ivr7.SolveRequest.SolveType.Evaluate; // now evaluate this sequence with the model reference (i.e. a minimal data-send)

        requestId = api.Post(sr);
        var EvalSolution = api.Get(requestId);

        // just print the infeasibilities
        ivr7helper.printSolution(EvalSolution);

        if (Math.Abs(EvalSolution.Objective - Solution.Objective) > 0.01f)
        {
            throw new Exception("Evaluation not identical to original solution value?");
        }
        // so this is pretty nice when we think about it. It means that if you want to evaluate
        // several permutations (i.e. modifications on a UI) then you don't have to resend the model each
        // time, you can send it only when the master data is modified (i.e. times, locations, tasks etc)
        // and then just use an evaluate solve request against a particular task-sequence.

        // for visualisations see the R/python notebook for plots on the same example.

        return;
    }