コード例 #1
0
void Operation::updateProblems()
{
  // Find all operationplans, and delegate the problem detection to them
  if (getDetectProblems())
    for (OperationPlan *o = first_opplan; o; o = o->next)
      o->updateProblems();
}
コード例 #2
0
ファイル: pegging.cpp プロジェクト: gaohaian/frePPLe
DECLARE_EXPORT PeggingIterator::PeggingIterator(const Demand* d)
  : downstream(false), firstIteration(true), first(false)
{
  initType(metadata);
  const Demand::OperationPlanList &deli = d->getDelivery();
  for (Demand::OperationPlanList::const_iterator opplaniter = deli.begin();
      opplaniter != deli.end(); ++opplaniter)
  {
    OperationPlan *t = (*opplaniter)->getTopOwner();
    updateStack(t, t->getQuantity(), 0.0, 0);
  }
}
コード例 #3
0
ファイル: buffer.cpp プロジェクト: BusinessTec/frePPLe
DECLARE_EXPORT void Buffer::setOnHand(double f)
{
  // The dummy operation to model the inventory may need to be created
  Operation *o = Operation::find(INVENTORY_OPERATION);
  Flow *fl;
  if (!o)
  {
    // Create a fixed time operation with zero leadtime, hidden from the xml
    // output, hidden for the solver, and without problem detection.
    o = new OperationFixedTime();
    o->setName(INVENTORY_OPERATION);
    o->setHidden(true);
    o->setDetectProblems(false);
    fl = new FlowEnd(o, this, 1);
  }
  else
    // Find the flow of this operation
    fl = const_cast<Flow*>(&*(o->getFlows().begin()));

  // Check valid pointers
  if (!fl || !o)
    throw LogicException("Failed creating inventory operation for '"
        + getName() + "'");

  // Make sure the sign of the flow is correct: +1 or -1.
  fl->setQuantity(f>=0.0 ? 1.0 : -1.0);

  // Create a dummy operationplan on the inventory operation
  OperationPlan::iterator i(o);
  if (i == OperationPlan::end())
  {
    // No operationplan exists yet
    OperationPlan *opplan = o->createOperationPlan(
        fabs(f), Date::infinitePast, Date::infinitePast);
    opplan->setLocked(true);
    opplan->activate();
  }
  else
  {
    // Update the existing operationplan
    i->setLocked(false);
    i->setQuantity(fabs(f));
    i->setLocked(true);
  }
  setChanged();
}
コード例 #4
0
PeggingIterator::PeggingIterator(const Demand* d)
    : downstream(false), firstIteration(true), first(false), second_pass(false)
{
    initType(metadata);
    const Demand::OperationPlanList &deli = d->getDelivery();
    for (Demand::OperationPlanList::const_iterator opplaniter = deli.begin();
            opplaniter != deli.end(); ++opplaniter)
    {
        OperationPlan *t = (*opplaniter)->getTopOwner();
        updateStack(t, t->getQuantity(), 0.0, 0);
    }

    // Bring all pegging information to a second stack.
    // Only in this way can we avoid that the same operationplan is returned
    // multiple times
    while (operator bool())
    {
        /** Check if already found in the vector. */
        bool found = false;
        state& curtop = states.back();
        for (deque<state>::iterator it = states_sorted.begin(); it != states_sorted.end() && !found; ++it)
            if (it->opplan == curtop.opplan)
            {
                // Update existing element in sorted stack
                it->quantity += curtop.quantity;
                if (it->level > curtop.level)
                    it->level = curtop.level;
                found = true;
            }
        if (!found)
            // New element in sorted stack
            states_sorted.push_back( state(curtop.opplan, curtop.quantity, curtop.offset, curtop.level) );

        if (downstream)
            ++*this;
        else
            --*this;
    }

    // The normal iteration will use the sorted results
    second_pass = true;
}
コード例 #5
0
ファイル: buffer.cpp プロジェクト: BusinessTec/frePPLe
DECLARE_EXPORT void Buffer::followPegging
(PeggingIterator& iter, FlowPlan* curflowplan, double qty, double offset, short lvl)
{
  if (!curflowplan->getOperationPlan()->getQuantity() || curflowplan->getBuffer()->getTool())
    // Flowplans with quantity 0 have no pegging.
    // Flowplans for buffers representing tools have no pegging either.
    return;

  Buffer::flowplanlist::iterator f = getFlowPlans().begin(curflowplan);
  if (curflowplan->getQuantity() < -ROUNDING_ERROR && !iter.isDownstream())
  {
    // CASE 1:
    // This is a flowplan consuming from a buffer. Navigating upstream means
    // finding the flowplans producing this consumed material.
    double scale = - curflowplan->getQuantity() / curflowplan->getOperationPlan()->getQuantity();
    double startQty = f->getCumulativeConsumed() + f->getQuantity() + offset * scale;
    double endQty = startQty + qty * scale;
    if (f->getCumulativeProduced() <= startQty + ROUNDING_ERROR)
    {
      // CASE 1A: Not produced enough yet: move forward
      while (f!=getFlowPlans().end()
          && f->getCumulativeProduced() <= startQty) ++f;
      while (f!=getFlowPlans().end()
          && ( (f->getQuantity()<=0 && f->getCumulativeProduced() < endQty)
              || (f->getQuantity()>0
                  && f->getCumulativeProduced()-f->getQuantity() < endQty))
            )
      {
        if (f->getQuantity() > ROUNDING_ERROR)
        {
          double newqty = f->getQuantity();
          double newoffset = 0.0;
          if (f->getCumulativeProduced()-f->getQuantity() < startQty)
          {
            newoffset = startQty - (f->getCumulativeProduced()-f->getQuantity());
            newqty -= newoffset;
          }
          if (f->getCumulativeProduced() > endQty)
            newqty -= f->getCumulativeProduced() - endQty;
          OperationPlan *opplan = dynamic_cast<const FlowPlan*>(&(*f))->getOperationPlan();
          OperationPlan *topopplan = opplan->getTopOwner();
          if (topopplan->getOperation()->getType() == *OperationSplit::metadata)
            topopplan = opplan;
          iter.updateStack(
            topopplan,
            topopplan->getQuantity() * newqty / f->getQuantity(),
            topopplan->getQuantity() * newoffset / f->getQuantity(),
            lvl
            );
        }
        ++f;
      }
    }
    else
    {
      // CASE 1B: Produced too much already: move backward
      while ( f!=getFlowPlans().end()
          && ((f->getQuantity()<=0 && f->getCumulativeProduced() > endQty)
              || (f->getQuantity()>0
                  && f->getCumulativeProduced()-f->getQuantity() > endQty))) --f;
      while (f!=getFlowPlans().end() && f->getCumulativeProduced() > startQty)
      {
        if (f->getQuantity() > ROUNDING_ERROR)
        {
          double newqty = f->getQuantity();
          double newoffset = 0.0;
          if (f->getCumulativeProduced()-f->getQuantity() < startQty)
          {
            newoffset = startQty - (f->getCumulativeProduced()-f->getQuantity());
            newqty -= newoffset;
          }
          if (f->getCumulativeProduced() > endQty)
            newqty -= f->getCumulativeProduced() - endQty;
          OperationPlan *opplan = dynamic_cast<FlowPlan*>(&(*f))->getOperationPlan();
          OperationPlan *topopplan = opplan->getTopOwner();
          if (topopplan->getOperation()->getType() == *OperationSplit::metadata)
            topopplan = opplan;
          iter.updateStack(
            topopplan,
            topopplan->getQuantity() * newqty / f->getQuantity(),
            topopplan->getQuantity() * newoffset / f->getQuantity(),
            lvl
            );
        }
        --f;
      }
    }
    return;
  }

  if (curflowplan->getQuantity() > ROUNDING_ERROR && iter.isDownstream())
  {
    // CASE 2:
    // This is a flowplan producing in a buffer. Navigating downstream means
    // finding the flowplans consuming this produced material.
    double scale = curflowplan->getQuantity() / curflowplan->getOperationPlan()->getQuantity();
    double startQty = f->getCumulativeProduced() - f->getQuantity() + offset * scale;
    double endQty = startQty + qty * scale;
    if (f->getCumulativeConsumed() <= startQty + ROUNDING_ERROR)
    {
      // CASE 2A: Not consumed enough yet: move forward
      while (f!=getFlowPlans().end()
          && f->getCumulativeConsumed() <= startQty) ++f;
      while (f!=getFlowPlans().end()
          && ( (f->getQuantity()<=0
              && f->getCumulativeConsumed()+f->getQuantity() < endQty)
              || (f->getQuantity()>0 && f->getCumulativeConsumed() < endQty))
            )
      {
        if (f->getQuantity() < -ROUNDING_ERROR)
        {
          double newqty = - f->getQuantity();
          double newoffset = 0.0;
          if (f->getCumulativeConsumed()+f->getQuantity() < startQty)
          {
            newoffset = startQty - (f->getCumulativeConsumed()+f->getQuantity());
            newqty -= newoffset;
          }
          if (f->getCumulativeConsumed() > endQty)
            newqty -= f->getCumulativeConsumed() - endQty;
          OperationPlan *opplan = dynamic_cast<FlowPlan*>(&(*f))->getOperationPlan();
          OperationPlan *topopplan = opplan->getTopOwner();
          if (topopplan->getOperation()->getType() == *OperationSplit::metadata)
            topopplan = opplan;
          iter.updateStack(
            topopplan,
            - topopplan->getQuantity() * newqty / f->getQuantity(),
            - topopplan->getQuantity() * newoffset / f->getQuantity(),
            lvl
            );
        }
        ++f;
      }
    }
    else
    {
      // CASE 2B: Consumed too much already: move backward
      while ( f!=getFlowPlans().end()
          && ((f->getQuantity()<=0 && f->getCumulativeConsumed()+f->getQuantity() < endQty)
              || (f->getQuantity()>0 && f->getCumulativeConsumed() < endQty))) --f;
      while (f!=getFlowPlans().end() && f->getCumulativeConsumed() > startQty)
      {
        if (f->getQuantity() < -ROUNDING_ERROR)
        {
          double newqty = - f->getQuantity();
          double newoffset = 0.0;
          if (f->getCumulativeConsumed()+f->getQuantity() < startQty)
            newqty -= startQty - (f->getCumulativeConsumed()+f->getQuantity());
          if (f->getCumulativeConsumed() > endQty)
            newqty -= f->getCumulativeConsumed() - endQty;
          OperationPlan *opplan = dynamic_cast<FlowPlan*>(&(*f))->getOperationPlan();
          OperationPlan *topopplan = opplan->getTopOwner();
          if (topopplan->getOperation()->getType() == *OperationSplit::metadata)
            topopplan = opplan;
          iter.updateStack(
            topopplan,
            - topopplan->getQuantity() * newqty / f->getQuantity(),
            - topopplan->getQuantity() * newoffset / f->getQuantity(),
            lvl
            );
        }
        --f;
      }
    }
  }
}
コード例 #6
0
ファイル: main.cpp プロジェクト: DwBu/frePPLe
int main (int argc, char *argv[])
{
  try
  {
    // 0: Initialize
    FreppleInitialize();

    // 1: Read the model
    FreppleReadXMLFile("problems.xml",true,false);
    reportProblems("reading input");

    // 2: Plan the model
    FreppleReadXMLData(
      "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" \
      "<plan xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n" \
      "<?python\n" \
      "frepple.solver_mrp(name=\"MRP\", constraints=0).solve()\n"  \
      "?>\n" \
      "</plan>", true, false
    );
    reportProblems("planning");

    // Define variables for each of the 2 operation_plans
    Operation *buildoper = Operation::find("make end item");
    OperationPlan *build = &*OperationPlan::iterator(buildoper);
    Operation *deliveroper = Operation::find("delivery end item");
    OperationPlan *deliver = &*OperationPlan::iterator(deliveroper);
    if (!deliver || !build) throw DataException("Can't find operationplans");

    // 3: Increase quantity of the delivery & report
    float oldqty = deliver->getQuantity();
    deliver->setQuantity(100);
    reportProblems("increasing delivery quantity");

    // 4: Reduce the quantity of the delivey & report
    deliver->setQuantity(1);
    reportProblems("decreasing delivery quantity");

    // 5: Move the delivery early & report
    Date oldstart = deliver->getDates().getStart();
    deliver->setStart(oldstart - Duration(86400));
    reportProblems("moving delivery early");

    // 6: Move the delivery late & report
    deliver->setStart(oldstart + Duration(86400));
    reportProblems("moving delivery late");

    // 7: Restoring original delivery plan & report
    deliver->setQuantity(oldqty);
    deliver->setStart(oldstart);
    reportProblems("restoring original delivery plan");

    // 8: Deleting delivery
    delete deliver;
    reportProblems("deleting delivery plan");

    // 9: Move the make operation before current & report
    oldstart = build->getDates().getStart();
    build->setStart(Plan::instance().getCurrent() - Duration(1));
    reportProblems("moving build early");

    // 10: Restoring the original build plan & report
    build->setStart(oldstart);
    reportProblems("restoring original build plan");
  }
  catch (...)
  {
    logger << "Error: Caught an exception in main routine:" <<  endl;
    try { throw; }
    catch (const exception& e) {logger << "  " << e.what() << endl;}
    catch (...) {logger << "  Unknown type" << endl;}
    return EXIT_FAILURE;
  }
  return EXIT_SUCCESS;
}
コード例 #7
0
ファイル: itemsupplier.cpp プロジェクト: bartyboy123/frePPLe
extern "C" PyObject* OperationItemSupplier::createOrder(
  PyObject *self, PyObject *args, PyObject *kwdict
  )
{
  // Parse the Python arguments
  PyObject* pylocation = NULL;
  unsigned long id = 0;
  const char* ref = NULL;
  PyObject* pyitem = NULL;
  PyObject* pysupplier = NULL;
  double qty = 0;
  PyObject* pystart = NULL;
  PyObject* pyend = NULL;
  const char* status = NULL;
  const char* source = NULL;
  static const char *kwlist[] = {
    "location", "id", "reference", "item", "supplier", "quantity", "start",
    "end", "status", "source", NULL
    };
  int ok = PyArg_ParseTupleAndKeywords(
    args, kwdict, "|OkzOOdOOzz:createOrder", const_cast<char**>(kwlist),
    &pylocation, &id, &ref, &pyitem, &pysupplier, &qty, &pystart,
    &pyend, &status, &source
    );
  if (!ok)
    return NULL;
  Date start = pystart ? PythonData(pystart).getDate() : Date::infinitePast;
  Date end = pyend ? PythonData(pyend).getDate() : Date::infinitePast;

  // Validate all arguments
  if (!pylocation || !pyitem)
  {
    PyErr_SetString(PythonDataException, "item and location arguments are mandatory");
    return NULL;
  }
  PythonData location_tmp(pylocation);
  if (!location_tmp.check(Location::metadata))
  {
    PyErr_SetString(PythonDataException, "location argument must be of type location");
    return NULL;
  }
  PythonData item_tmp(pyitem);
  if (!item_tmp.check(Item::metadata))
  {
    PyErr_SetString(PythonDataException, "item argument must be of type item");
    return NULL;
  }
  PythonData supplier_tmp(pysupplier);
  if (pysupplier && !supplier_tmp.check(Supplier::metadata))
  {
    PyErr_SetString(PythonDataException, "supplier argument must be of type supplier");
    return NULL;
  }
  Item *item = static_cast<Item*>(item_tmp.getObject());
  Location *location = static_cast<Location*>(location_tmp.getObject());
  Supplier *supplier = pysupplier ? static_cast<Supplier*>(supplier_tmp.getObject()) : NULL;

  // Find or create the destination buffer.
  Buffer* destbuffer = NULL;
  Item::bufferIterator buf_iter(item);
  while (Buffer* tmpbuf = buf_iter.next())
  {
    if (tmpbuf->getLocation() == location)
    {
      if (destbuffer)
      {
        stringstream o;
        o << "Multiple buffers found for item '" << item << "'' and location'" << location << "'";
        throw DataException(o.str());
      }
      destbuffer = tmpbuf;
    }
  }
  if (!destbuffer)
  {
    // Create the destination buffer
    destbuffer = new BufferDefault();
    stringstream o;
    o << item << " @ " << location;
    destbuffer->setName(o.str());
    destbuffer->setItem(item);
    destbuffer->setLocation(location);
  }

  // Build the producing operation for this buffer.
  destbuffer->getProducingOperation();

  // Look for a matching operation replenishing this buffer.
  Operation *oper = NULL;
  for (Buffer::flowlist::const_iterator flowiter = destbuffer->getFlows().begin();
    flowiter != destbuffer->getFlows().end() && !oper; ++flowiter)
  {
    if (flowiter->getOperation()->getType() != *OperationItemSupplier::metadata)
      continue;
    OperationItemSupplier* opitemsupplier = static_cast<OperationItemSupplier*>(flowiter->getOperation());
    if (supplier)
    {
      if (supplier->isMemberOf(opitemsupplier->getItemSupplier()->getSupplier()))
        oper = opitemsupplier;
    }
    else
      oper = opitemsupplier;
  }

  // No matching operation is found.
  if (!oper)
  {
    // We'll create one now, but that requires that we have a supplier defined.
    if (!supplier)
      throw DataException("Supplier is needed on this purchase order");
    // Note: We know that we need to create a new one. An existing one would
    // have created an operation on the buffer already.
    ItemSupplier *itemsupplier = new ItemSupplier();
    itemsupplier->setSupplier(supplier);
    itemsupplier->setItem(item);
    itemsupplier->setLocation(location);
    oper = new OperationItemSupplier(itemsupplier, destbuffer);
    new ProblemInvalidData(oper, "Purchase orders on unauthorized supplier", "operation",
      Date::infinitePast, Date::infiniteFuture, 1);
  }

  // Finally, create the operationplan
  OperationPlan *opplan = oper->createOperationPlan(qty, start, end);
  if (id)
    opplan->setRawIdentifier(id);  // We can use this fast method because we call activate later
  if (status)
    opplan->setStatus(status);
  // Reset quantity after the status update to assure that
  // also non-valid quantities are getting accepted.
  opplan->setQuantity(qty);
  if (ref)
    opplan->setReference(ref);
  opplan->activate();

  // Return result
  Py_INCREF(opplan);
  return opplan;
}
コード例 #8
0
ファイル: loadplan.cpp プロジェクト: zhoufoxcn/frePPLe
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();
}
コード例 #9
0
ファイル: itemsupplier.cpp プロジェクト: gaohaian/frePPLe
extern "C" PyObject* OperationItemSupplier::createOrder(
  PyObject *self, PyObject *args, PyObject *kwdict
  )
{
  // Parse the Python arguments
  PyObject* pylocation = NULL;
  unsigned long id = 0;
  const char* ref = NULL;
  PyObject* pyitem = NULL;
  PyObject* pysupplier = NULL;
  double qty = 0;
  PyObject* pystart = NULL;
  PyObject* pyend = NULL;
  const char* status = NULL;
  const char* source = NULL;
  static const char *kwlist[] = {
    "location", "id", "reference", "item", "supplier", "quantity", "start",
    "end", "status", "source", NULL
    };
  int ok = PyArg_ParseTupleAndKeywords(
    args, kwdict, "|OkzOOdOOzz:createOrder", const_cast<char**>(kwlist),
    &pylocation, &id, &ref, &pyitem, &pysupplier, &qty, &pystart,
    &pyend, &status, &source
    );
  if (!ok)
    return NULL;
  Date start = pystart ? PythonData(pystart).getDate() : Date::infinitePast;
  Date end = pyend ? PythonData(pyend).getDate() : Date::infinitePast;

  // Validate all arguments
  if (!pylocation || !pyitem)
  {
    PyErr_SetString(PythonDataException, "item and location arguments are mandatory");
    return NULL;
  }
  PythonData location_tmp(pylocation);
  if (!location_tmp.check(Location::metadata))
  {
    PyErr_SetString(PythonDataException, "location argument must be of type location");
    return NULL;
  }
  PythonData item_tmp(pyitem);
  if (!item_tmp.check(Item::metadata))
  {
    PyErr_SetString(PythonDataException, "item argument must be of type item");
    return NULL;
  }
  PythonData supplier_tmp(pysupplier);
  if (pysupplier && !supplier_tmp.check(Supplier::metadata))
  {
    PyErr_SetString(PythonDataException, "supplier argument must be of type supplier");
    return NULL;
  }
  Item *item = static_cast<Item*>(item_tmp.getObject());
  Location *location = static_cast<Location*>(location_tmp.getObject());
  Supplier *supplier = pysupplier ? static_cast<Supplier*>(supplier_tmp.getObject()) : NULL;

  // Find or create the destination buffer.
  Buffer* destbuffer = NULL;
  for (Buffer::iterator bufiter = Buffer::begin(); bufiter != Buffer::end(); ++bufiter)
  {
    if (bufiter->getLocation() == location && bufiter->getItem() == item)
    {
      if (destbuffer)
      {
        stringstream o;
        o << "Multiple buffers found for item '" << item << "'' and location'" << location << "'";
        throw DataException(o.str());
      }
      destbuffer = &*bufiter;
    }
  }
  if (!destbuffer)
  {
    // Create the destination buffer
    destbuffer = new BufferDefault();
    stringstream o;
    o << item << " @ " << location;
    destbuffer->setName(o.str());
    destbuffer->setItem(item);
    destbuffer->setLocation(location);
  }

  // Look for a matching matching supplying operation on this buffer.
  // Here we also trigger the creation of its producing operation, which
  // contains the logic to build possible transfer operations.
  Operation *oper = NULL;
  Operation* prodOper = destbuffer->getProducingOperation();
  if (prodOper && prodOper->getType() == *OperationItemSupplier::metadata)
  {
    if (supplier)
    {
      if (supplier->isMemberOf(static_cast<OperationItemSupplier*>(prodOper)->getItemSupplier()->getSupplier()))
        oper = prodOper;
    }
    else
      oper = prodOper;
  }
  else if (prodOper && prodOper->getType() == *OperationAlternate::metadata)
  {
    SubOperation::iterator soperiter = prodOper->getSubOperationIterator();
    while (SubOperation *soper = soperiter.next())
    {
      if (soper->getType() == *OperationItemSupplier::metadata)
      {
        if (supplier)
        {
          if (supplier->isMemberOf(static_cast<OperationItemSupplier*>(prodOper)->getItemSupplier()->getSupplier()))
          {
            oper = soper->getOperation();
            break;
          }
        }
        else
        {
          oper = prodOper;
          break;
        }
      }
    }
  }

  // No matching operation is found.
  if (!oper)
  {
    // We'll create one now, but that requires that we have a supplier defined.
    if (!supplier)
      throw DataException("Supplier is needed on this purchase order");
    // Note: We know that we need to create a new one. An existing one would
    // have created an operation on the buffer already.
    ItemSupplier *itemsupplier = new ItemSupplier();
    itemsupplier->setSupplier(supplier);
    itemsupplier->setItem(item);
    itemsupplier->setLocation(location);
    oper = new OperationItemSupplier(itemsupplier, destbuffer);
    new ProblemInvalidData(oper, "Purchase orders on unauthorized supplier", "operation",
      Date::infinitePast, Date::infiniteFuture, 1);
  }

  // Finally, create the operationplan
  OperationPlan *opplan = oper->createOperationPlan(qty, start, end);
  if (status)
    opplan->setStatus(status);
  if (ref)
    opplan->setReference(ref);

  // Return result
  Py_INCREF(opplan);
  return opplan;
}
コード例 #10
0
ファイル: itemdistribution.cpp プロジェクト: Rona111/frePPLe
extern "C" PyObject* OperationItemDistribution::createOrder(
  PyObject *self, PyObject *args, PyObject *kwdict
  )
{
  // Parse the Python arguments
  PyObject* pydest = NULL;
  unsigned long id = 0;
  const char* ref = NULL;
  PyObject* pyitem = NULL;
  PyObject* pyorigin = NULL;
  double qty = 0;
  PyObject* pystart = NULL;
  PyObject* pyend = NULL;
  int consume = 1;
  const char* status = NULL;
  const char* source = NULL;
  static const char *kwlist[] = {
    "destination", "id", "reference", "item", "origin", "quantity", "start",
    "end", "consume_material", "status", "source", NULL
    };
  int ok = PyArg_ParseTupleAndKeywords(
    args, kwdict, "|OkzOOdOOpzz:createOrder", const_cast<char**>(kwlist),
    &pydest, &id, &ref, &pyitem, &pyorigin, &qty, &pystart, &pyend,
    &consume, &status, &source
    );
  if (!ok)
    return NULL;
  Date start = pystart ? PythonData(pystart).getDate() : Date::infinitePast;
  Date end = pyend ? PythonData(pyend).getDate() : Date::infinitePast;

  // Validate all arguments
  if (!pydest || !pyitem)
  {
    PyErr_SetString(PythonDataException, "item and destination arguments are mandatory");
    return NULL;
  }
  PythonData dest_tmp(pydest);
  if (!dest_tmp.check(Location::metadata))
  {
    PyErr_SetString(PythonDataException, "destination argument must be of type location");
    return NULL;
  }
  PythonData item_tmp(pyitem);
  if (!item_tmp.check(Item::metadata))
  {
    PyErr_SetString(PythonDataException, "item argument must be of type item");
    return NULL;
  }
  PythonData origin_tmp(pyorigin);
  if (pyorigin && !origin_tmp.check(Location::metadata))
  {
    PyErr_SetString(PythonDataException, "origin argument must be of type location");
    return NULL;
  }
  Item *item = static_cast<Item*>(item_tmp.getObject());
  Location *dest = static_cast<Location*>(dest_tmp.getObject());
  Location *origin = pyorigin ? static_cast<Location*>(origin_tmp.getObject()) : NULL;

  // Find or create the destination buffer.
  Buffer* destbuffer = NULL;
  for (Buffer::iterator bufiter = Buffer::begin(); bufiter != Buffer::end(); ++bufiter)
  {
    if (bufiter->getLocation() == dest && bufiter->getItem() == item)
    {
      if (destbuffer)
      {
        stringstream o;
        o << "Multiple buffers found for item '" << item << "'' and location'" << dest << "'";
        throw DataException(o.str());
      }
      destbuffer = &*bufiter;
    }
  }
  if (!destbuffer)
  {
    // Create the destination buffer
    destbuffer = new BufferDefault();
    stringstream o;
    o << item << " @ " << dest;
    destbuffer->setName(o.str());
    destbuffer->setItem(item);
    destbuffer->setLocation(dest);
  }

  // Build the producing operation for this buffer.
  destbuffer->getProducingOperation();

  // Look for a matching operation replenishing this buffer.
  Operation *oper = NULL;
  for (Buffer::flowlist::const_iterator flowiter = destbuffer->getFlows().begin();
    flowiter != destbuffer->getFlows().end() && !oper; ++flowiter)
  {
    if (flowiter->getOperation()->getType() != *OperationItemDistribution::metadata
      || flowiter->getQuantity() <= 0)
        continue;
    OperationItemDistribution* opitemdist = static_cast<OperationItemDistribution*>(flowiter->getOperation());
    if (origin)
    {
      // Origin must match as well
      for (Operation::flowlist::const_iterator fl = opitemdist->getFlows().begin();
          fl != opitemdist->getFlows().end(); ++ fl)
      {
        if (fl->getQuantity() < 0 && fl->getBuffer()->getLocation()->isMemberOf(origin))
          oper = opitemdist;
      }
    }
    else
      oper = opitemdist;
  }

  // No matching operation is found.
  if (!oper)
  {
    // We'll create one now, but that requires that we have an origin defined.
    if (!origin)
      throw DataException("Origin location is needed on this distribution order");
    Buffer* originbuffer = NULL;
    for (Buffer::iterator bufiter = Buffer::begin(); bufiter != Buffer::end(); ++bufiter)
    {
      if (bufiter->getLocation() == origin && bufiter->getItem() == item)
      {
        if (originbuffer)
        {
          stringstream o;
          o << "Multiple buffers found for item '" << item << "'' and location'" << dest << "'";
          throw DataException(o.str());
        }
        originbuffer = &*bufiter;
      }
    }
    if (!originbuffer)
    {
      // Create the origin buffer
      originbuffer = new BufferDefault();
      stringstream o;
      o << item << " @ " << origin;
      originbuffer->setName(o.str());
      originbuffer->setItem(item);
      originbuffer->setLocation(origin);
    }
    // Note: We know that we need to create a new one. An existing one would
    // have created an operation on the buffer already.
    ItemDistribution *itemdist = new ItemDistribution();
    itemdist->setOrigin(origin);
    itemdist->setItem(item);
    itemdist->setDestination(dest);
    oper = new OperationItemDistribution(itemdist, originbuffer, destbuffer);
    new ProblemInvalidData(oper, "Distribution orders on unauthorized lanes", "operation",
      Date::infinitePast, Date::infiniteFuture, 1);
  }

  // Finally, create the operationplan
  OperationPlan *opplan = oper->createOperationPlan(qty, start, end, NULL, NULL, 0, false);
  if (id)
    opplan->setIdentifier(id);
  if (status)
    opplan->setStatus(status);
  if (ref)
    opplan->setReference(ref);
  if (!consume)
    opplan->setConsumeMaterial(false);
  opplan->activate();

  // Return result
  Py_INCREF(opplan);
  return opplan;
}
コード例 #11
0
bool OperationPlan::updateFeasible()
{
  if (!getOperation()->getDetectProblems())
  {
    // No problems to be flagged on this operation
    setFeasible(true);
    return true;
  }

  // The implementation of this method isn't really cleanly object oriented. It uses
  // logic which only the different resource and buffer implementation classes should be
  // aware.
  if (firstsubopplan)
  {
    // Check feasibility of child operationplans
    for (OperationPlan *i = firstsubopplan; i; i = i->nextsubopplan)
    {
      if (!i->updateFeasible())
      {
        setFeasible(false);
        return false;
      }
    }
  }
  else
  {
    // Before current and before fence problems are only detected on child operationplans
    if (getConfirmed())
    {
      if (dates.getEnd() < Plan::instance().getCurrent())
      {
        // Before current violation
        setFeasible(false);
        return false;
      }
    }
    else
    {
      if (dates.getStart() < Plan::instance().getCurrent())
      {
        // Before current violation
        setFeasible(false);
        return false;
      }
      else if (dates.getStart() < Plan::instance().getCurrent() + oper->getFence() && getProposed())
      {
        // Before fence violation
        setFeasible(false);
        return false;
      }
    }
  }
  if (nextsubopplan
    && getEnd() > nextsubopplan->getStart() + Duration(1L)
    && !nextsubopplan->getConfirmed()
    && owner && !owner->getOperation()->hasType<OperationSplit>()
    )
  {
    // Precedence violation
    // Note: 1 second grace period for precedence problems to avoid rounding issues
    setFeasible(false);
    return false;
  }

  // Verify the capacity constraints
  for (auto ldplan = getLoadPlans(); ldplan != endLoadPlans(); ++ldplan)
  {
    if (ldplan->getResource()->hasType<ResourceDefault>() && ldplan->getQuantity() > 0)
    {
      auto curMax = ldplan->getMax();
      for (
        auto cur = ldplan->getResource()->getLoadPlans().begin(&*ldplan);
        cur != ldplan->getResource()->getLoadPlans().end();
        ++cur
        )
      {
        if (cur->getOperationPlan() == this && cur->getQuantity() < 0)
          break;
        if (cur->getEventType() == 4)
          curMax = cur->getMax(false);
        if (
          cur->getEventType() != 5
          && cur->isLastOnDate()
          && cur->getOnhand() > curMax + ROUNDING_ERROR
          )
        {
          // Overload on default resource
          setFeasible(false);
          return false;
        }
      }
    }
    else if (ldplan->getResource()->hasType<ResourceBuckets>())
    {
      for (
        auto cur = ldplan->getResource()->getLoadPlans().begin(&*ldplan);
        cur != ldplan->getResource()->getLoadPlans().end() && cur->getEventType() != 2;
        ++cur
        )
      {
        if (cur->getOnhand() < -ROUNDING_ERROR)
        {
          // Overloaded capacity on bucketized resource
          setFeasible(false);
          return false;
        }
      }
    }
  }

  // Verify the material constraints
  for (auto flplan = beginFlowPlans(); flplan != endFlowPlans(); ++flplan)
  {
    if (
      !flplan->getFlow()->isConsumer()
      || flplan->getBuffer()->hasType<BufferInfinite>()
      )
      continue;
    auto flplaniter = flplan->getBuffer()->getFlowPlans();
    for (auto cur = flplaniter.begin(&*flplan); cur != flplaniter.end(); ++cur)
    {
      if (cur->getOnhand() < -ROUNDING_ERROR && cur->isLastOnDate())
      {
        // Material shortage
        setFeasible(false);
        return false;
      }
    }
  }

  // After all checks, it turns out to be feasible
  setFeasible(true);
  return true;
}