IBM ILOG Dispatcher User's Manual > Field Service Solutions > Dispatching Technicians > Model

Once you have written a description of your problem, you can use Dispatcher classes to model it.

Step 2   -  

Open the example file

Open the example file YourDispatcherHome/examples/src/tutorial/technic2_partial.cpp in your development environment.

As in the previous examples, you will use a RoutingModel class to call the functions that create the dimensions, nodes, vehicles (technicians), and visits.

Step 3   -  

Declare the RoutingModel class

Add the following code after the comment //Declare the RoutingModel class.

class RoutingModel {
  IloEnv              _env;
  IloModel            _mdl;

  void createDimensions();
  void createIloNodes(char* nodeFileName);
  void createTechnicians(char* technicianFileName);
  void createVisits(char* visitsFileName);
  void setVisitsSkillPenalty(char * skillCostsFileName,
                             char * visitSkillsFileName,
                             char * techSkillsFileName);

The function createDimensions defines the skillPenalty dimension. The createTechnicians function is similar to the createVehicles function of previous lessons, in that it adds an IloVehicle to the model. The function setVisitsSkillPenalty calculates the cost of each visit and adds it to the model. The other functions are similar to their counterparts in the previous lessons.

Step 4   -  

Create the dimensions

Add the following code after the comment //Create the dimensions.

void RoutingModel::createDimensions() {
  IloDimension1 skillPenalty(_env, IloFalse, "SkillPenalty");
  _mdl.add(skillPenalty);
  skillPenalty.setKey("SkillPenalty");
  IloDimension2 time(_env, IloEuclidean, "Time");
  time.setKey("Time");
  _mdl.add(time);
}

Instances of IloDimension1 are intrinsic dimensions; this means that the value of the dimension depends only on the object itself (such as an object's weight), and not on any other factors. The dimension skillPenalty will be used to assign a cost to the visit of each vehicle, just as weight or capacity were used in previous lessons. The second parameter is set to IloFalse in order to speed up search; this is permissible as no constraints are posted on variables related to the skillPenalty. However, skillPenalty can still be used in the cost function.

The function createTechnicians adds vehicles named technician to the model.

Step 5   -  

Create the vehicle/technicians

    IloVehicle technician(first, last, name);
    technician.setCost(time, 1.0);
    technician.setCost(skillPenalty, 1.0);
    technician.setKey(name);
    _mdl.add(technician);

Add the following code after the comment //Create the vehicle/technicians.

As there is only one technician per vehicle, it is convenient to represent the vehicle with the name technician. This section of createTechnicians sets the cost for the vehicles based on the dimensions of time and skillPenalty.

The first section of createTechnicians is similar to what you have worked with in the createVehicles function of previous lessons, with the addition of some code to ensure that a skillPenalty of zero is associated with the first and last visits to the depot.

void RoutingModel::createTechnicians(char* techFileName) {
  IloDimension2 time = IloDimension2::Find(_env, "Time");
  IloDimension1 skillPenalty = IloDimension1::Find(_env, "SkillPenalty");
  IloCsvReader csvTechReader(_env, techFileName);
  IloCsvReader::LineIterator it(csvTechReader);
  while(it.ok()) {
    IloCsvLine line = *it;
    char * namefirst = line.getStringByHeader("first");
    char * namelast = line.getStringByHeader("last");
    char * name = line.getStringByHeader("name");
    IloNum openTime = line.getFloatByHeader("open");
    IloNum closeTime = line.getFloatByHeader("close");
    IloNode node1 = IloNode::Find(_env, namefirst);
    IloNode node2 = IloNode::Find(_env, namelast);

    IloVisit first(node1, "depot");
    _mdl.add(first.getTransitVar(skillPenalty) == 0);
    _mdl.add(first.getCumulVar(time) >= openTime);

    IloVisit last(node2, "depot");
    _mdl.add(last.getCumulVar(time) <= closeTime);
    _mdl.add(last.getTransitVar(skillPenalty) == 0);

Step 6   -  

Create the setVisitsSkillPenalty function

Add the following code after the comment
//Create the setVisitsSkillPenalty function.

void RoutingModel::setVisitsSkillPenalty(char * skillCostsFileName,
                                         char * visitSkillsFileName,
                                         char * techSkillsFileName) {
  IloDimension1 skillPenalty = IloDimension1::Find(_env, "SkillPenalty");
  IloCsvReader csvVisitSkillsReader(_env, visitSkillsFileName);
  IloCsvReader csvTechSkillsReader(_env, techSkillsFileName);
  IloCsvReader csvSkillCostsReader(_env, skillCostsFileName);
  IloInt nbOfTech = csvTechSkillsReader.getNumberOfItems();
  IloVehicleArray techs(_env, nbOfTech);
  IloInt i = 0;
  IloCsvReader::LineIterator  it(csvTechSkillsReader);
  while(it.ok()){
    IloCsvLine line = *it;
    char * techName = line.getStringByHeader("name");
    IloVehicle technician = IloVehicle::Find(_env, techName);
    techs[i] = technician;
    i++;
    ++it;
  }

This section of the function creates the vehicle array techs with the vehicle instances technician. The IloCsvReader instance csvVisitSkillsReader reads the skills required at each customer visit; csvTechSkillsReader reads the skill level of each technician; and csvSkillCostsReader reads the cost for each technician skill type to perform a visit to a customer site.

The next section of setVisitsSkillPenalty creates an array of the cost for each visit, named visitCost. To create this array, the visit skill required for each visit (visitSkillName) is read from a csv file. The cost to make this visit with the various techSkill levels is read with csvSkillCostsReader as an IloCsvLine costline. Then in Iterator it3 the value in costline corresponding to the appropriate techSkillName is stored in the array visitCost.

  IloCsvReader::LineIterator  it2(csvVisitSkillsReader);
  while(it2.ok()){
    IloCsvLine line = *it2;
    char * visitName = line.getStringByHeader("name");
    IloVisit visit = IloVisit::Find(_env, visitName);
    char * visitSkillName = line.getStringByHeader("VisitSkillName");
    IloCsvLine costline = csvSkillCostsReader.getLineByKey(1, visitSkillName);
    IloNumArray visitCost(_env, nbOfTech);
    IloInt k = 0;
    IloCsvReader::LineIterator  it3(csvTechSkillsReader);
    while(it3.ok()){
      IloCsvLine line1 = *it3;
      char * techSkillName = line1.getStringByHeader("TechSkillName");
      visitCost[k] = costline.getFloatByHeader(techSkillName);
      k++;
      ++it3;
    }

Step 7   -  

Add the skill cost

Add the following code after the comment //Add the skill cost.

    IloVehicleToNumFunction function(_env, techs, visitCost);
    IloNumVar skillCostVar (function(visit.getVehicleVar()));
    _mdl.add(visit.getTransitVar(skillPenalty) == skillCostVar);

In this section of setVisitsSkillPenalty, the IloVehicleToNumFunction constructor associates the values of the array visitCost to the vehicle/technicians of the array techs. Then visit.getVehicleVar returns the vehicle/technician for the visit, and the cost for that visit is applied to the IloNumVar skillCostVar. Finally, this is added to the model in the dimension skillPenalty.