DECLARE_EXPORT PyObject* SetupMatrix::addPythonRule(PyObject* self, PyObject* args, PyObject* kwdict) { try { // Pick up the setup matrix SetupMatrix *matrix = static_cast<SetupMatrix*>(self); if (!matrix) throw LogicException("Can't add a rule to a NULL setupmatrix"); // Parse the arguments int prio = 0; PyObject *pyfrom = NULL; PyObject *pyto = NULL; long duration = 0; double cost = 0; static const char *kwlist[] = {"priority", "fromsetup", "tosetup", "duration", "cost", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwdict, "i|ssld:addRule", const_cast<char**>(kwlist), &prio, &pyfrom, &pyto, &duration, &cost)) return NULL; // Add the new rule Rule * r = new Rule(matrix, prio); if (pyfrom) r->setFromSetup(PythonObject(pyfrom).getString()); if (pyto) r->setToSetup(PythonObject(pyfrom).getString()); r->setDuration(duration); r->setCost(cost); return PythonObject(r); } catch(...) { PythonType::evalException(); return NULL; } }
DECLARE_EXPORT PyObject* Solver::getattro(const Attribute& attr) { if (attr.isA(Tags::tag_name)) return PythonObject(getName()); if (attr.isA(Tags::tag_loglevel)) return PythonObject(getLogLevel()); return NULL; }
DECLARE_EXPORT PyObject* Skill::getattro(const Attribute& attr) { if (attr.isA(Tags::tag_name)) return PythonObject(getName()); if (attr.isA(Tags::tag_resourceskills)) return new ResourceSkillIterator(this); if (attr.isA(Tags::tag_source)) return PythonObject(getSource()); return NULL; }
DECLARE_EXPORT PyObject* ResourceSkill::getattro(const Attribute& attr) { if (attr.isA(Tags::tag_resource)) return PythonObject(getResource()); if (attr.isA(Tags::tag_skill)) return PythonObject(getSkill()); if (attr.isA(Tags::tag_priority)) return PythonObject(getPriority()); if (attr.isA(Tags::tag_effective_end)) return PythonObject(getEffective().getEnd()); if (attr.isA(Tags::tag_effective_start)) return PythonObject(getEffective().getStart()); return NULL; }
DECLARE_EXPORT PyObject* SetupMatrix::getattro(const Attribute& attr) { if (attr.isA(Tags::tag_name)) return PythonObject(getName()); if (attr.isA(Tags::tag_rules)) return new SetupMatrixRuleIterator(this); return NULL; }
DECLARE_EXPORT PyObject* SolverMRP::getattro(const Attribute& attr) { if (attr.isA(Tags::tag_constraints)) return PythonObject(getConstraints()); if (attr.isA(Tags::tag_autocommit)) return PythonObject(getAutocommit()); if (attr.isA(Tags::tag_userexit_flow)) return getUserExitFlow(); if (attr.isA(Tags::tag_userexit_demand)) return getUserExitDemand(); if (attr.isA(Tags::tag_userexit_buffer)) return getUserExitBuffer(); if (attr.isA(Tags::tag_userexit_resource)) return getUserExitResource(); if (attr.isA(Tags::tag_userexit_operation)) return getUserExitOperation(); if (attr.isA(Tags::tag_plantype)) return PythonObject(getPlanType()); // Less common parameters if (attr.isA(tag_iterationthreshold)) return PythonObject(getIterationThreshold()); if (attr.isA(tag_iterationaccuracy)) return PythonObject(getIterationAccuracy()); if (attr.isA(tag_lazydelay)) return PythonObject(getLazyDelay()); if (attr.isA(tag_planSafetyStockFirst)) return PythonObject(getPlanSafetyStockFirst()); // Default parameters return Solver::getattro(attr); }
/** @todo this method implementation is not generic enough and not extendible by subclasses. */ PyObject* ResourceSkill::create(PyTypeObject* pytype, PyObject* args, PyObject* kwds) { try { // Pick up the skill PyObject* skill = PyDict_GetItemString(kwds,"skill"); if (!PyObject_TypeCheck(skill, Skill::metadata->pythonClass)) throw DataException("resourceskill skill must be of type skill"); // Pick up the resource PyObject* res = PyDict_GetItemString(kwds,"resource"); if (!PyObject_TypeCheck(res, Resource::metadata->pythonClass)) throw DataException("resourceskill resource must be of type resource"); // Pick up the priority PyObject* q1 = PyDict_GetItemString(kwds,"priority"); int q2 = q1 ? PythonObject(q1).getInt() : 1; // Pick up the effective dates DateRange eff; PyObject* eff_start = PyDict_GetItemString(kwds,"effective_start"); if (eff_start) { PythonObject d(eff_start); eff.setStart(d.getDate()); } PyObject* eff_end = PyDict_GetItemString(kwds,"effective_end"); if (eff_end) { PythonObject d(eff_end); eff.setEnd(d.getDate()); } // Create the load ResourceSkill *l = new ResourceSkill( static_cast<Skill*>(skill), static_cast<Resource*>(res), q2, eff ); // Return the object Py_INCREF(l); return static_cast<PyObject*>(l); } catch (...) { PythonType::evalException(); return NULL; } }
DECLARE_EXPORT PyObject* eraseModel(PyObject* self, PyObject* args) { // Pick up arguments PyObject *obj = NULL; int ok = PyArg_ParseTuple(args, "|O:erase", &obj); if (!ok) return NULL; // Validate the argument bool deleteStaticModel = false; if (obj) deleteStaticModel = PythonObject(obj).getBool(); // Execute and catch exceptions Py_BEGIN_ALLOW_THREADS // Free Python interpreter for other threads try { if (deleteStaticModel) { // Delete all entities. // The order is chosen to minimize the work of the individual destructors. // E.g. the destructor of the item class recurses over all demands and // all buffers. It is much faster if there are none already. Demand::clear(); Operation::clear(); Buffer::clear(); Resource::clear(); SetupMatrix::clear(); Location::clear(); Customer::clear(); Calendar::clear(); Solver::clear(); Item::clear(); // The setup operation is a static singleton and should always be around OperationSetup::setupoperation = Operation::add(new OperationSetup("setup operation")); } else // Delete the operationplans only for (Operation::iterator gop = Operation::begin(); gop != Operation::end(); ++gop) gop->deleteOperationPlans(); } catch (...) { Py_BLOCK_THREADS; PythonType::evalException(); return NULL; } Py_END_ALLOW_THREADS // Reclaim Python interpreter return Py_BuildValue(""); }
PyObject* Problem::getattro(const Attribute& attr) { if (attr.isA(Tags::tag_name)) return PythonObject(getType().type); if (attr.isA(Tags::tag_description)) return PythonObject(getDescription()); if (attr.isA(Tags::tag_entity)) return PythonObject(getEntity()); if (attr.isA(Tags::tag_start)) return PythonObject(getDates().getStart()); if (attr.isA(Tags::tag_end)) return PythonObject(getDates().getEnd()); if (attr.isA(Tags::tag_weight)) return PythonObject(getWeight()); if (attr.isA(Tags::tag_owner)) return PythonObject(getOwner()); return NULL; }
PyObject* FlowPlan::getattro(const Attribute& attr) { if (attr.isA(Tags::tag_operationplan)) return PythonObject(getOperationPlan()); if (attr.isA(Tags::tag_quantity)) return PythonObject(getQuantity()); if (attr.isA(Tags::tag_flow)) return PythonObject(getFlow()); if (attr.isA(Tags::tag_date)) return PythonObject(getDate()); if (attr.isA(Tags::tag_onhand)) return PythonObject(getOnhand()); if (attr.isA(Tags::tag_buffer)) // Convenient shortcut return PythonObject(getFlow()->getBuffer()); if (attr.isA(Tags::tag_operation)) // Convenient shortcut return PythonObject(getFlow()->getOperation()); return NULL; }
DECLARE_EXPORT PyObject* Item::getattro(const Attribute& attr) { if (attr.isA(Tags::tag_name)) return PythonObject(getName()); if (attr.isA(Tags::tag_description)) return PythonObject(getDescription()); if (attr.isA(Tags::tag_category)) return PythonObject(getCategory()); if (attr.isA(Tags::tag_subcategory)) return PythonObject(getSubCategory()); if (attr.isA(Tags::tag_price)) return PythonObject(getPrice()); if (attr.isA(Tags::tag_owner)) return PythonObject(getOwner()); if (attr.isA(Tags::tag_operation)) return PythonObject(getOperation()); if (attr.isA(Tags::tag_hidden)) return PythonObject(getHidden()); if (attr.isA(Tags::tag_members)) return new ItemIterator(this); return NULL; }
DECLARE_EXPORT PyObject* SetupMatrix::Rule::getattro(const Attribute& attr) { if (attr.isA(Tags::tag_priority)) return PythonObject(priority); if (attr.isA(Tags::tag_setupmatrix)) return PythonObject(matrix); if (attr.isA(Tags::tag_fromsetup)) return PythonObject(from); if (attr.isA(Tags::tag_tosetup)) return PythonObject(to); if (attr.isA(Tags::tag_duration)) return PythonObject(duration); if (attr.isA(Tags::tag_cost)) return PythonObject(cost); return NULL; }
DECLARE_EXPORT void SolverMRP::solve(const Flow* fl, void* v) // @todo implement search mode { // Note: This method is only called for consuming flows and for the leading // flow of an alternate group. See SolverMRP::checkOperation SolverMRPdata* data = static_cast<SolverMRPdata*>(v); if (fl->hasAlternates()) { // CASE I: It is an alternate flow. // We ask each alternate flow in order of priority till we find a flow // that has a non-zero reply. // 1) collect a list of alternates list<const Flow*> thealternates; const Flow *x = fl->hasAlternates() ? fl : fl->getAlternate(); for (Operation::flowlist::const_iterator i = fl->getOperation()->getFlows().begin(); i != fl->getOperation()->getFlows().end(); ++i) if ((i->getAlternate() == x || &*i == x) && i->getEffective().within(data->state->q_flowplan->getDate())) thealternates.push_front(&*i); // 2) Sort the list thealternates.sort(sortFlow); // 3) Control the planning mode bool originalPlanningMode = data->constrainedPlanning; data->constrainedPlanning = true; const Flow *firstAlternate = NULL; double firstQuantity = 0.0; // Remember the top constraint bool originalLogConstraints = data->logConstraints; //Problem* topConstraint = data->planningDemand->getConstraints().top(); // 4) Loop through the alternates till we find a non-zero reply Date min_next_date(Date::infiniteFuture); double ask_qty; FlowPlan *flplan = data->state->q_flowplan; for (list<const Flow*>::const_iterator i = thealternates.begin(); i != thealternates.end();) { const Flow *curflow = *i; data->state->q_flowplan = flplan; // because q_flowplan can change // 4a) Switch to this flow if (data->state->q_flowplan->getFlow() != curflow) data->state->q_flowplan->setFlow(curflow); // 4b) Call the Python user exit if there is one if (userexit_flow) { PythonObject result = userexit_flow.call(data->state->q_flowplan, PythonObject(data->constrainedPlanning)); if (!result.getBool()) { // Return value is false, alternate rejected if (data->getSolver()->getLogLevel()>1) logger << indent(curflow->getOperation()->getLevel()) << " User exit disallows consumption from '" << (*i)->getBuffer()->getName() << "'" << endl; // Move to the next alternate if (++i != thealternates.end() && data->getSolver()->getLogLevel()>1) logger << indent(curflow->getOperation()->getLevel()) << " Alternate flow switches from '" << curflow->getBuffer()->getName() << "' to '" << (*i)->getBuffer()->getName() << "'" << endl; continue; } } // Remember the first alternate if (!firstAlternate) { firstAlternate = *i; firstQuantity = data->state->q_flowplan->getQuantity(); } // Constraint tracking if (*i != firstAlternate) // Only enabled on first alternate data->logConstraints = false; else // Keep track of constraints, if enabled data->logConstraints = originalLogConstraints; // 4c) Ask the buffer data->state->q_qty = ask_qty = - data->state->q_flowplan->getQuantity(); data->state->q_date = data->state->q_flowplan->getDate(); CommandManager::Bookmark* topcommand = data->setBookmark(); curflow->getBuffer()->solve(*this,data); // 4d) A positive reply: exit the loop if (data->state->a_qty > ROUNDING_ERROR) { // Update the opplan, which is required to (1) update the flowplans // and to (2) take care of lot sizing constraints of this operation. if (data->state->a_qty < ask_qty - ROUNDING_ERROR) { flplan->setQuantity(-data->state->a_qty, true); data->state->a_qty = -flplan->getQuantity(); } if (data->state->a_qty > ROUNDING_ERROR) { data->constrainedPlanning = originalPlanningMode; data->logConstraints = originalLogConstraints; return; } } // 4e) Undo the plan on the alternate data->rollback(topcommand); // 4f) Prepare for the next alternate if (data->state->a_date < min_next_date) min_next_date = data->state->a_date; if (++i != thealternates.end() && data->getSolver()->getLogLevel()>1) logger << indent(curflow->getOperation()->getLevel()) << " Alternate flow switches from '" << curflow->getBuffer()->getName() << "' to '" << (*i)->getBuffer()->getName() << "'" << endl; } // 5) No reply found, all alternates are infeasible if (!originalPlanningMode) { assert(firstAlternate); // Unconstrained plan: Plan on the primary alternate // Switch to this flow if (flplan->getFlow() != firstAlternate) flplan->setFlow(firstAlternate); // Message if (data->getSolver()->getLogLevel()>1) logger << indent(fl->getOperation()->getLevel()) << " Alternate flow plans unconstrained on alternate '" << firstAlternate->getBuffer()->getName() << "'" << endl; // Plan unconstrained data->constrainedPlanning = false; data->state->q_flowplan = flplan; // because q_flowplan can change flplan->setQuantity(firstQuantity, true); data->state->q_qty = ask_qty = - flplan->getQuantity(); data->state->q_date = flplan->getDate(); firstAlternate->getBuffer()->solve(*this,data); data->state->a_qty = -flplan->getQuantity(); // Restore original planning mode data->constrainedPlanning = originalPlanningMode; } else { // Constrained plan: Return 0 data->state->a_date = min_next_date; data->state->a_qty = 0; if (data->getSolver()->getLogLevel()>1) logger << indent(fl->getOperation()->getLevel()) << " Alternate flow doesn't find supply on any alternate : " << data->state->a_qty << " " << data->state->a_date << endl; } } else { // CASE II: Not an alternate flow. // In this case, this method is passing control on to the buffer. data->state->q_qty = - data->state->q_flowplan->getQuantity(); data->state->q_date = data->state->q_flowplan->getDate(); if (data->state->q_qty != 0.0) { fl->getBuffer()->solve(*this,data); if (data->state->a_date > fl->getEffective().getEnd()) { // The reply date must be less than the effectivity end date: after // that date the flow in question won't consume any material any more. if (data->getSolver()->getLogLevel()>1 && data->state->a_qty < ROUNDING_ERROR) logger << indent(fl->getBuffer()->getLevel()) << " Buffer '" << fl->getBuffer()->getName() << "' answer date is adjusted to " << fl->getEffective().getEnd() << " because of a date effective flow" << endl; data->state->a_date = fl->getEffective().getEnd(); } } else { // It's a zero quantity flowplan. // E.g. because it is not effective. data->state->a_date = data->state->q_date; data->state->a_qty = 0.0; } } }
PythonObject PythonObject::attr(const std::string& name) const { return PythonObject(owning{}, PyObject_GetAttrString(obj_, name.c_str())); }
PythonObject construct(long long ll) { return PythonObject(PythonObject::owning {}, PyLong_FromLongLong(ll)); }
PythonObject construct(int i) { return PythonObject(PythonObject::owning {}, PyInt_FromLong(i)); }
PythonObject construct(double d) { return PythonObject(PythonObject::owning {}, PyFloat_FromDouble(d)); }
PythonObject construct(const std::string& str) { return PythonObject(PythonObject::owning {}, PyString_FromString(str.c_str())); }