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

The complete program follows. You can also view it online in the file YourDispatcherHome/examples/src/technic2_complete.cpp.

// -------------------------------------------------------------- -*- C++ -*-
// File: examples/src/technic2.cpp
//---------------------------------------------------------------------------

#include <ildispat/ilodispatcher.h>

ILOSTLBEGIN

///////////////////////////////////////////////////////////////////////////////
// Modeling
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);

public:
  RoutingModel(IloEnv env,
               int argc,
               char* argv[]);
  ~RoutingModel() {}
  IloEnv getEnv() const { return _env; }
  IloModel getModel() const { return _mdl; }
};

RoutingModel::RoutingModel(IloEnv env,
                                       int argc,
                                       char * argv[])
  : _env(env), _mdl(env){

  createDimensions();

  char* nodeFileName;
  if(argc < 4)
    nodeFileName = (char *) "../../../examples/data/technic2/nodes.csv";
  else
    nodeFileName = argv[3];
  createIloNodes(nodeFileName);

  char* technicianFileName;
  if (argc < 2)
    technicianFileName = (char *) "../../../examples/data/technic2/technicians.csv";
  else
    technicianFileName = argv[1];
  createTechnicians(technicianFileName);

  char* visitsFileName;
  if (argc < 3)
    visitsFileName =
      (char*) "../../../examples/data/technic2/visits.csv";
  else
    visitsFileName = argv[2];
  createVisits(visitsFileName);

  //set the correct skillPenalty for visits
  char * visitSkillsFileName = (char *) "../../../examples/data/technic2/visitSkills.csv";
  char * techSkillsFileName = (char *) "../../../examples/data/technic2/techSkills.csv";
  char * skillCostsFileName = (char *) "../../../examples/data/technic2/skillCosts.csv";

  if(argc >= 4)
    visitSkillsFileName = argv[4];
  if(argc >= 5) {
    visitSkillsFileName = argv[4];
    techSkillsFileName  = argv[5];
  }
  if(argc >= 6) {
    visitSkillsFileName = argv[4];
    techSkillsFileName  = argv[5];
    skillCostsFileName  = argv[6];
  }
  setVisitsSkillPenalty(skillCostsFileName, visitSkillsFileName, techSkillsFileName);

}

// Add 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);
}

// Create IloNodes
void RoutingModel::createIloNodes(char* nodeFileName) {
  IloCsvReader csvNodeReader(_env, nodeFileName);
  IloCsvReader::LineIterator  it(csvNodeReader);
  while(it.ok()) {
    IloCsvLine line = *it;
    char* name = line.getStringByHeader("name");
    IloNode node(_env,
                 line.getFloatByHeader("x"),
                 line.getFloatByHeader("y"),
                 0,
                 name);
    node.setKey(name);
    ++it;
  }
  csvNodeReader.end();
}

// Create technicians
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);

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

    ++it;
  }
  csvTechReader.end();
}

// Create visits
void RoutingModel::createVisits(char* visitsFileName) {
  IloDimension2 time = IloDimension2::Find(_env, "Time");
  IloCsvReader csvVisitReader(_env, visitsFileName);
  IloCsvReader::LineIterator  it(csvVisitReader);
  while(it.ok()){
    IloCsvLine line = *it;
    //read visit data from files
    char * visitName = line.getStringByHeader("name");
    char * nodeName = line.getStringByHeader("node");
    IloNum minTime  = line.getFloatByHeader("minTime");
    IloNum maxTime  = line.getFloatByHeader("maxTime");
    IloNum dropTime = line.getFloatByHeader("dropTime");
    IloNode node = IloNode::Find(_env, nodeName);
    IloVisit visit(node, visitName);
    _mdl.add(visit.getDelayVar(time) == dropTime);
    _mdl.add(minTime <= visit.getCumulVar(time) <= maxTime);
    visit.setKey(visitName);
    _mdl.add(visit);
    ++it;
  }
  csvVisitReader.end();
}

//set the correct skillPenalty for visits
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;
  }

  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;
    }

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

    ++it2;
  }
  csvVisitSkillsReader.end();
  csvTechSkillsReader.end();
  csvSkillCostsReader.end();

}

///////////////////////////////////////////////////////////////////////////////
// Solving
class RoutingSolver {
  IloModel            _mdl;
  IloSolver           _solver;
  IloRoutingSolution  _solution;

  IloBool findFirstSolution(IloGoal goal);
  void greedyImprove(IloNHood nhood, IloGoal subGoal);
  void improve(IloGoal subgoal);
public:
  RoutingSolver(RoutingModel mdl);
  ~RoutingSolver() {}
  IloRoutingSolution getSolution() const { return _solution; }
  void printInformation() const;
  void solve();
};

RoutingSolver::RoutingSolver(RoutingModel mdl)
  : _mdl(mdl.getModel()), _solver(mdl.getModel()), _solution(mdl.getModel()) {}

// Solving : find first solution
IloBool RoutingSolver::findFirstSolution(IloGoal goal) {
  if (!_solver.solve(goal)) {
    _solver.error() << "Infeasible Routing Plan" << endl;
    return IloFalse;
  }
  IloDispatcher dispatcher(_solver);
  _solver.out() << dispatcher.getTotalCost() << endl;
  _solution.store(_solver);
  return IloTrue;
}

// Improve solution using nhood
void RoutingSolver::greedyImprove(IloNHood nhood, IloGoal subGoal) {
  _solver.out() << "Improving solution" << endl;
  IloEnv env = _solver.getEnv();
  nhood.reset();
  IloGoal improve = IloSingleMove(env,
                                  _solution,
                                  nhood,
                                  IloImprove(env),
                                  subGoal);
  while (_solver.solve(improve)) {}
}

// Improve solution
void RoutingSolver::improve(IloGoal subGoal) {
  IloEnv env = _solver.getEnv();
  IloNHood nhood = IloTwoOpt(env)
                 + IloOrOpt(env)
                 + IloRelocate(env)
                 + IloCross(env)
                 + IloExchange(env);
  greedyImprove(nhood, subGoal);
}

// Display Dispatcher information
void RoutingSolver::printInformation() const {
  IloDispatcher dispatcher(_solver);
  _solver.printInformation();
  dispatcher.printInformation();
  _solver.out() << "===============" << endl
                << "Cost         : " << dispatcher.getTotalCost() << endl
                << "Number of vehicles used : "
                << dispatcher.getNumberOfVehiclesUsed() << endl
                << "Solution     : " << endl
                << dispatcher << endl;
}

// Solving
void RoutingSolver::solve() {
  IloDispatcher dispatcher(_solver);
  IloEnv env = _solver.getEnv();
  // Subgoal
  IloGoal instantiateCost = IloDichotomize(env,
                                           dispatcher.getCostVar(),
                                           IloFalse);
  IloGoal restoreSolution = IloRestoreSolution(env, _solution);
  IloGoal goal = IloInsertionGenerate(env, instantiateCost);

  // Solving
  if (findFirstSolution(goal)) {
    improve(instantiateCost);
    _solver.solve(restoreSolution);
  }
}

///////////////////////////////////////////////////////////////////////////////

int main(int argc, char * argv[]) {
  IloEnv env;
  try {
    RoutingModel mdl(env, argc, argv);
    RoutingSolver solver(mdl);
    solver.solve();
    solver.printInformation();
  } catch(IloException& ex) {
    cerr << "Error: " << ex << endl;
  }
  env.end();
  return 0;
}