Ejemplo n.º 1
0
DECLARE_EXPORT void LoadPlan::setResource(Resource* newres, bool check)
{
  // Nothing to do
  if (res == newres) return;

  // Validate the argument
  if (!newres) throw DataException("Can't switch to NULL resource");
  if (check)
  {
    // New resource must be a subresource of the load's resource.
    bool ok = false;
    for (const Resource* i = newres; i && !ok; i = i->getOwner())
      if (i == getLoad()->getResource()) ok = true;
    if (!ok)
      throw DataException("Resource isn't matching the resource specified on the load");

    // New resource must have the required skill
    if (getLoad()->getSkill())
    {
      ok = false;
      for(Resource::skilllist::const_iterator s = newres->getSkills().begin();
        s != newres->getSkills().end() && !ok; s++)
        if (s->getSkill() == getLoad()->getSkill()) ok = true;
      if (!ok)
        throw DataException("Resource misses the skill specified on the load");
    }
  }

  // Mark entities as changed
  if (oper) oper->getOperation()->setChanged();
  if (res && res!=newres) res->setChanged();
  newres->setChanged();

  // Update also the setup operationplans
  if (oper && oper->getOperation() != OperationSetup::setupoperation)
  {
    bool oldHasSetup = ld && !ld->getSetup().empty()  // TODO not fully correct. If the load is changed, it is still possible that the old load had a setup, while ld doesn't have one any more...
        && res && res->getSetupMatrix();
    bool newHasSetup = ld && !ld->getSetup().empty()
        && newres->getSetupMatrix();
    OperationPlan *setupOpplan = NULL;
    if (oldHasSetup)
    {
      for (OperationPlan::iterator i(oper); i != oper->end(); ++i)
        if (i->getOperation() == OperationSetup::setupoperation)
        {
          setupOpplan = &*i;
          break;
        }
      if (!setupOpplan) oldHasSetup = false;
    }
    if (oldHasSetup)
    {
      if (newHasSetup)
      {
        // Case 1: Both the old and new load require a setup
        LoadPlan *setupLdplan = NULL;
        for (OperationPlan::LoadPlanIterator j = setupOpplan->beginLoadPlans();
            j != setupOpplan->endLoadPlans(); ++j)
          if (j->getLoad() == ld)
          {
            setupLdplan = &*j;
            break;
          }
        if (!setupLdplan)
          throw LogicException("Can't find loadplan on setup operationplan");
        // Update the loadplan
        setupOpplan->setEnd(setupOpplan->getDates().getEnd());
      }
      else
      {
        // Case 2: Delete the old setup which is not required any more
        oper->eraseSubOperationPlan(setupOpplan);
      }
    }
    else
    {
      if (newHasSetup)
      {
        // Case 3: Create a new setup operationplan
        OperationSetup::setupoperation->createOperationPlan(
          1, Date::infinitePast, oper->getDates().getEnd(), NULL, oper);
      }
      //else:
      // Case 4: No setup for the old or new load
    }
  }

  // Find the loadplan before the setup
  LoadPlan *prevldplan = NULL;
  if (getOperationPlan()->getOperation() == OperationSetup::setupoperation)
  {
    for (TimeLine<LoadPlan>::const_iterator i = getResource()->getLoadPlans().begin(isStart() ? getOtherLoadPlan() : this);
        i != getResource()->getLoadPlans().end(); --i)
    {
      const LoadPlan *l = dynamic_cast<const LoadPlan*>(&*i);
      if (l && l->getOperationPlan() != getOperationPlan()
          && l->getOperationPlan() != getOperationPlan()->getOwner()
          && !l->isStart())
      {
        prevldplan = const_cast<LoadPlan*>(l);
        break;
      }
    }
    if (!prevldplan)
    {
      for (TimeLine<LoadPlan>::const_iterator i = getResource()->getLoadPlans().begin(isStart() ? getOtherLoadPlan() : this);
          i != getResource()->getLoadPlans().end(); ++i)
      {
        const LoadPlan *l = dynamic_cast<const LoadPlan*>(&*i);
        if (l && l->getOperationPlan() != getOperationPlan()
            && l->getOperationPlan() != getOperationPlan()->getOwner()
            && !l->isStart())
        {
          prevldplan = const_cast<LoadPlan*>(l);
          break;
        }
      }
    }
  }

  // Change this loadplan and its brother
  for (LoadPlan *ldplan = getOtherLoadPlan(); true; )
  {
    // Remove from the old resource, if there is one
    if (res)
    {
      res->loadplans.erase(ldplan);
      res->setChanged();
    }

    // Insert in the new resource.
    // This code assumes the date and quantity of the loadplan don't change
    // when a new resource is assigned.
    ldplan->res = newres;
    newres->loadplans.insert(
      ldplan,
      ld->getLoadplanQuantity(ldplan),
      ld->getLoadplanDate(ldplan)
    );

    // Repeat for the brother loadplan or exit
    if (ldplan != this) ldplan = this;
    else break;
  }

  // Update the setups on the old resource
  if (prevldplan) prevldplan->res->updateSetups(prevldplan);

  // Change the resource
  newres->setChanged();
}
Ejemplo n.º 2
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;
}