IBM ILOG Scheduler User's Manual > Advanced Concepts > Scheduling with State Resources: the Trolley Problem > Defining the Problem, Designing a Model > Jobs and Activities

The code that follows defines a new class, Job, used to represent the activities associated with the processing of an item.

class Job {
private:
  const char* _name;
  IloActivity _loadA;
  IloActivity _unload1;
  IloActivity _process1;
  IloActivity _load1;
  IloActivity _unload2;
  IloActivity _process2;
  IloActivity _load2;
  IloActivity _unloadS;
  IloInt      _area1;
  IloInt      _area2;
  IloUnaryResource _machine1;
  IloUnaryResource _machine2;
public:
  Job(IloEnv env,
      const char*,
      IloNum,
      IloUnaryResource, IloNum, IloInt,
      IloUnaryResource, IloNum, IloInt);
  ~Job();
  void addToModel(IloModel model,
                  IloStateResource trolley,
                  IloNumVar makespan);
  void printSolution(IlcScheduler scheduler, ILOSTD(ostream)& out);
  const char* getName() const { return  _name;}
 
  IloActivity getLoadA() const { return _loadA;}
  IloActivity getUnload1() const { return _unload1;}
  IloActivity getProcess1() const { return _process1;}
  IloActivity getLoad1() const { return _load1;}
  IloActivity getUnload2() const { return _unload2;}
  IloActivity getProcess2() const { return _process2;}
  IloActivity getLoad2() const { return _load2;}
  IloActivity getUnloadS() const { return _unloadS;}
 
};
 

As stated earlier, a job consists of a sequence of eight activities. The fields _area1 and _area2 respectively store the areas of the first and the second machine required to process the item.

The constructor of an instance of Job is implemented below. The code shows how to implement the constraint so that load/unload activities require the trolley to be in a given position. The constructor also constrains the makespan variable to be greater than or equal to the job completion time.

Job::Job(IloEnv env,
         const char*  name,
         IloNum loadDuration,
         IloUnaryResource machine1, IloNum duration1, IloInt area1,
         IloUnaryResource machine2, IloNum duration2, IloInt area2)
  : _name(name),
    _loadA(   env,loadDuration),
    _unload1( env,loadDuration),
    _process1(env,duration1),
    _load1(   env,loadDuration),
    _unload2( env,loadDuration),
    _process2(env,duration2),
    _load2(   env,loadDuration),
    _unloadS( env,loadDuration),
    _area1(area1),
    _area2(area2),
    _machine1(machine1),
    _machine2(machine2)
{
  char buffer[256];
 
  sprintf(buffer, "arrival_load_%s", name);
  _loadA.setName(buffer);
 
  sprintf(buffer, "area%ld_unload_%s", area1, name);
  _unload1.setName(buffer);
 
  sprintf(buffer, "machine%ld_%s", area1, name);
  _process1.setName(buffer);
 
  sprintf(buffer, "area%ld_load_%s", area1, name);
  _load1.setName(buffer);
 
  sprintf(buffer, "area%ld_unload_%s", area2, name);
  _unload2.setName(buffer);
 
  sprintf(buffer, "machine%ld_%s", area2, name);
  _process2.setName(buffer);
 
  sprintf(buffer, "area%ld_load_%s", area2, name);
  _load2.setName(buffer);
  
  sprintf(buffer, "stock_load_%s", name);
  _unloadS.setName(buffer);
 
}
 
Job::~Job()
{}
 
void Job::addToModel(IloModel model, 
                     IloStateResource trolley,
                     IloNumVar makespan) 
{
  /* ADD MACHINE REQUIREMENT CONSTRAINTS */
  model.add(_process1.requires(_machine1));
  model.add(_process2.requires(_machine2));
 
  /* ADD TEMPORAL CONSTRAINTS BETWEEN ACTIVITIES */
  model.add(_unload1.startsAfterEnd(_loadA));
  model.add(_process1.startsAfterEnd(_unload1));
  model.add(_load1.startsAfterEnd(_process1));
  model.add(_unload2.startsAfterEnd(_load1));
  model.add(_process2.startsAfterEnd(_unload2));
  model.add(_load2.startsAfterEnd(_process2));
  model.add(_unloadS.startsAfterEnd(_load2));
 
  /* ADD TROLLEY POSITION REQUIREMENTS  */
  model.add(_loadA.requires(trolley, (IloAny)AREAA));
  model.add(_unload1.requires(trolley, (IloAny)_area1));
  model.add(_load1.requires(trolley, (IloAny)_area1));
  model.add(_unload2.requires(trolley, (IloAny)_area2));
  model.add(_load2.requires(trolley, (IloAny)_area2));
  model.add(_unloadS.requires(trolley, (IloAny)AREAS));
 
  /* ADD MAKESPAN CONSTRAINT */
  model.add(_unloadS.endsBefore(makespan));
}
 
 

The function that follows displays a job on the out() stream of the solver.

void Job::printSolution(IlcScheduler scheduler, ILOSTD(ostream)& out) 
{
  /* PRINT JOB */
  out
    << "JOB " << _name << endl
    << "\t Load at area A: " 
    << scheduler.getActivity( _loadA ) << endl
    << "\t Unload at area " << (long)_area1 << ": " 
    << scheduler.getActivity( _unload1 )   << endl
    << "\t Process on machine " << (long)_area1 << ": " 
    << scheduler.getActivity( _process1 )  << endl
    << "\t Load at area "   << (long)_area1 << ": " 
    << scheduler.getActivity( _load1 )     << endl
    << "\t Unload at area " << (long)_area2 << ": " 
    << scheduler.getActivity( _unload2 )   << endl
    << "\t Process on machine " << (long)_area2 << ": " 
    << scheduler.getActivity( _process2 )  << endl
    << "\t Load at area "   << (long)_area2 << " : " 
    << scheduler.getActivity( _load2 )     << endl
    << "\t Unload at area S: "
    << scheduler.getActivity( _unloadS )   << endl;
}
    

That function is used by the function PrintSolution to print the schedule, along with a list of the chronological positions of the trolley.

 
void PrintSolution(IloSolver solver,
                   IloAnyArray jobs,
                   IloStateResource trolley,
                   IloArray<IloUnaryResource> machines,
                   IloNumVar makespan)
{
  IlcScheduler scheduler(solver);
 
  solver.out() << "Solution with makespan " << solver.getMin(makespan) << endl;
  IloInt numberOfJobs = jobs.getSize();
  for (IloInt i = 0; i < numberOfJobs; i++)
    ((Job*) jobs[i])->printSolution(scheduler, solver.out());
 
  // Output from algorithm
  solver.out() << "TROLLEY POSITIONS: " << endl;
 
  IlcAnyTimetable tt = scheduler.getStateResource(trolley).getTimetable();
  for (IlcAnyTimetableCursor cr(tt,0); cr.ok(); ++cr)
    if (cr.isBound())
      solver.out() << "\t [" << cr.getTimeMin()
                   << "," << cr.getTimeMax() 
                   << "): Position " << (long)cr.getValue()
                   << endl;
  solver.printInformation();
 
#if defined(ILO_SDXLOUTPUT)
  IloSDXLTrolleyOutput output(solver.getEnv());
  ofstream outFile("trolley1.xml");
  output.writeTrolley(scheduler, 
                      outFile, 
                      jobs, 
                      trolley,
                      (IloInt) 1000,
                      machines, 
                      makespan);
  outFile.close();
#endif 
  
}
 

In the definition of the scheduling problem, the jobs of the problem are created and stored in an array as follows.

  /* CREATION OF JOB INSTANCES */
  jobs = IloAnyArray(env, numberOfJobs);
  for (i = 0; i < numberOfJobs; i++) {
    Job* job = new (env) Job(env,
                             jobNames[i],
                             loadDuration,
                             machines[machines1[i] - 1], 
                             durations1[i], machines1[i], 
                             machines[machines2[i] - 1], 
                             durations2[i], machines2[i]);
    jobs[i] = job;
    job->addToModel(model, trolley, makespan);
  }