Beispiel #1
0
DECLARE_EXPORT void SolverMRP::chooseResource(const Load* l, void* v)   // @todo handle unconstrained plan!!!!
{
  if (!l->getSkill() && !l->getResource()->isGroup())
  {
    // CASE 1: No skill involved, and no aggregate resource either
    l->getResource()->solve(*this, v);
    return;
  }

  // CASE 2: Skill involved, or aggregate resource
  SolverMRPdata* data = static_cast<SolverMRPdata*>(v);
  unsigned int loglevel = data->getSolver()->getLogLevel();

  // Control the planning mode
  bool originalPlanningMode = data->constrainedPlanning;
  data->constrainedPlanning = true;

  // Don't keep track of the constraints right now
  bool originalLogConstraints = data->logConstraints;
  data->logConstraints = false;

  // Initialize
  Date min_next_date(Date::infiniteFuture);
  LoadPlan *lplan = data->state->q_loadplan;
  Resource *bestAlternateSelection = NULL;
  double bestCost = DBL_MAX;
  bool qualified_resource_exists = false;
  double bestAlternateValue = DBL_MAX;
  double bestAlternateQuantity = DBL_MIN;
  double beforeCost = data->state->a_cost;
  double beforePenalty = data->state->a_penalty;
  OperationPlanState originalOpplan(lplan->getOperationPlan());
  double originalLoadplanQuantity = lplan->getQuantity();
  data->getSolver()->setLogLevel(0);  // Silence during this loop

  // Loop over all candidate resources
  stack<Resource*> res_stack;
  res_stack.push(l->getResource());
  while (!res_stack.empty())
  {
    // Pick next resource
    Resource* res = res_stack.top();
    res_stack.pop();

    // If it's an aggregate, push it's members on the stack
    if (res->isGroup())
    {
      for (Resource::memberIterator x = res->beginMember(); x != Resource::end(); ++x)
        res_stack.push(&*x);
      continue;
    }

    // Check if the resource has the right skill
    if (l->getSkill())
    {
      bool isqualified = false;
      for (Resource::skilllist::const_iterator i = res->getSkills().begin();
        i != res->getSkills().end() && !isqualified; ++i)
      {
        if (i->getSkill() == l->getSkill()
            && originalOpplan.start >= i->getEffective().getStart()
            && originalOpplan.end <= i->getEffective().getEnd())
          isqualified = true;
      }
      // Next resource in the loop if not qualified
      if (!isqualified) continue;   // TODO if there is a date effective skill, we need to consider it in the reply
    }
    qualified_resource_exists = true;

    // Switch to this resource
    data->state->q_loadplan = lplan; // because q_loadplan can change!
    lplan->setResource(res);
    lplan->getOperationPlan()->restore(originalOpplan);
    data->state->q_qty = lplan->getQuantity();
    data->state->q_date = lplan->getDate();

    // Plan the resource
    CommandManager::Bookmark* topcommand = data->setBookmark();
    try { res->solve(*this,data); }
    catch (...)
    {
      data->getSolver()->setLogLevel(loglevel);
      data->constrainedPlanning = originalPlanningMode;
      data->logConstraints = originalLogConstraints;
      data->rollback(topcommand);
      throw;
    }
    data->rollback(topcommand);

    // Evaluate the result
    if (data->state->a_qty > ROUNDING_ERROR
        && lplan->getOperationPlan()->getQuantity() > 0)
    {
      double deltaCost = data->state->a_cost - beforeCost;
      double deltaPenalty = data->state->a_penalty - beforePenalty;
      // Message
      if (loglevel>1)
        logger << indent(l->getOperation()->getLevel()) << "   Operation '"
            << l->getOperation()->getName() << "' evaluates alternate '"
            << res << "': cost " << deltaCost
            << ", penalty " << deltaPenalty << endl;
      data->state->a_cost = beforeCost;
      data->state->a_penalty = beforePenalty;
      double val = 0.0;
      switch (l->getSearch())
      {
      case PRIORITY:
          val = 1; // @todo skill-resource model doesn't have a priority field yet
          break;
        case MINCOST:
          val = deltaCost / lplan->getOperationPlan()->getQuantity();
          break;
        case MINPENALTY:
          val = deltaPenalty / lplan->getOperationPlan()->getQuantity();
          break;
        case MINCOSTPENALTY:
          val = (deltaCost + deltaPenalty) / lplan->getOperationPlan()->getQuantity();
          break;
        default:
          LogicException("Unsupported search mode for alternate load");
      }
      if (val + ROUNDING_ERROR < bestAlternateValue
          || (fabs(val - bestAlternateValue) < ROUNDING_ERROR
              && lplan->getOperationPlan()->getQuantity() > bestAlternateQuantity))
      {
        // Found a better alternate
        bestAlternateValue = val;
        bestAlternateSelection = res;
        bestAlternateQuantity = lplan->getOperationPlan()->getQuantity();
      }
    }
    else if (loglevel>1)
      logger << indent(l->getOperation()->getLevel()) << "   Operation '"
          << l->getOperation()->getName() << "' evaluates alternate '"
          << lplan->getResource() << "': not available before "
          << data->state->a_date << endl;

    // Keep track of best next date
    if (data->state->a_date < min_next_date)
      min_next_date = data->state->a_date;
  }
  data->getSolver()->setLogLevel(loglevel);

  // Not a single resource has the appropriate skills. You're joking?
  if (!qualified_resource_exists)
    throw DataException("No qualified resource exists for load");

  // Restore the best candidate we found in the loop above
  if (bestAlternateSelection)
  {
    // Message
    if (loglevel>1)
      logger << indent(l->getOperation()->getLevel()) << "   Operation '"
          << l->getOperation()->getName() << "' chooses alternate '"
          << bestAlternateSelection << "' " << l->getSearch() << endl;

    // Switch back
    data->state->q_loadplan = lplan; // because q_loadplan can change!
    data->state->a_cost = beforeCost;
    data->state->a_penalty = beforePenalty;
    if (lplan->getResource() != bestAlternateSelection)
      lplan->setResource(bestAlternateSelection);
    lplan->getOperationPlan()->restore(originalOpplan);
    data->state->q_qty = lplan->getQuantity();
    data->state->q_date = lplan->getDate();
    bestAlternateSelection->solve(*this,data);

    // Restore the planning mode
    data->constrainedPlanning = originalPlanningMode;
    data->logConstraints = originalLogConstraints;
    return;
  }

  // 7) No alternate gave a good result
  data->state->a_date = min_next_date;
  data->state->a_qty = 0;

  // Restore the planning mode
  data->constrainedPlanning = originalPlanningMode;

  // Maintain the constraint list
  if (originalLogConstraints)
  {
    data->planningDemand->getConstraints().push(
      ProblemCapacityOverload::metadata,
      l->getResource(), originalOpplan.start, originalOpplan.end,
      -originalLoadplanQuantity);
  }
  data->logConstraints = originalLogConstraints;

  if (loglevel>1)
    logger << indent(lplan->getOperationPlan()->getOperation()->getLevel()) <<
        "   Alternate load doesn't find supply on any alternate : "
        << data->state->a_qty << "  " << data->state->a_date << endl;
}