flow.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002   file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/tags/0.9.1/src/model/flow.cpp $
00003   version : $LastChangedRevision: 1656 $  $LastChangedBy: jdetaeye $
00004   date : $LastChangedDate: 2012-03-27 19:05:34 +0200 (Tue, 27 Mar 2012) $
00005  ***************************************************************************/
00006 
00007 /***************************************************************************
00008  *                                                                         *
00009  * Copyright (C) 2007-2012 by Johan De Taeye, frePPLe bvba                 *
00010  *                                                                         *
00011  * This library is free software; you can redistribute it and/or modify it *
00012  * under the terms of the GNU Lesser General Public License as published   *
00013  * by the Free Software Foundation; either version 2.1 of the License, or  *
00014  * (at your option) any later version.                                     *
00015  *                                                                         *
00016  * This library is distributed in the hope that it will be useful,         *
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of          *
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser *
00019  * General Public License for more details.                                *
00020  *                                                                         *
00021  * You should have received a copy of the GNU Lesser General Public        *
00022  * License along with this library; if not, write to the Free Software     *
00023  * Foundation Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 *
00024  * USA                                                                     *
00025  *                                                                         *
00026  ***************************************************************************/
00027 
00028 #define FREPPLE_CORE
00029 #include "frepple/model.h"
00030 namespace frepple
00031 {
00032 
00033 DECLARE_EXPORT const MetaCategory* Flow::metadata;
00034 DECLARE_EXPORT const MetaClass* FlowStart::metadata,
00035                *FlowEnd::metadata;
00036 
00037 
00038 int Flow::initialize()
00039 {
00040   // Initialize the metadata
00041   metadata = new MetaCategory
00042   ("flow", "flows", MetaCategory::ControllerDefault, writer);
00043   FlowStart::metadata = new MetaClass("flow", "flow_start",
00044       Object::createDefault<FlowStart>, true);
00045   FlowEnd::metadata = new MetaClass("flow", "flow_end",
00046       Object::createDefault<FlowEnd>);
00047 
00048   // Initialize the type
00049   PythonType& x = FreppleCategory<Flow>::getType();
00050   x.setName("flow");
00051   x.setDoc("frePPLe flow");
00052   x.supportgetattro();
00053   x.supportsetattro();
00054   x.supportcreate(create);
00055   x.addMethod("toXML", toXML, METH_VARARGS, "return a XML representation");
00056   const_cast<MetaCategory*>(metadata)->pythonClass = x.type_object();
00057   return x.typeReady();
00058 }
00059 
00060 
00061 void Flow::writer(const MetaCategory* c, XMLOutput* o)
00062 {
00063   bool firstflow = true;
00064   for (Operation::iterator i = Operation::begin(); i != Operation::end(); ++i)
00065     for (Operation::flowlist::const_iterator j = i->getFlows().begin(); j != i->getFlows().end(); ++j)
00066     {
00067       if (firstflow)
00068       {
00069         o->BeginObject(Tags::tag_flows);
00070         firstflow = false;
00071       }
00072       // We use the FULL mode, to force the flows being written regardless
00073       // of the depth in the XML tree.
00074       o->writeElement(Tags::tag_flow, &*j, FULL);
00075     }
00076   if (!firstflow) o->EndObject(Tags::tag_flows);
00077 }
00078 
00079 
00080 DECLARE_EXPORT void Flow::validate(Action action)
00081 {
00082   // Catch null operation and buffer pointers
00083   Operation* oper = getOperation();
00084   Buffer* buf = getBuffer();
00085   if (!oper || !buf)
00086   {
00087     // This flow is not a valid one since it misses essential information
00088     if (!oper && !buf)
00089       throw DataException("Missing operation and buffer on a flow");
00090     else if (!oper)
00091       throw DataException("Missing operation on a flow with buffer '"
00092           + buf->getName() + "'");
00093     else
00094       throw DataException("Missing buffer on a flow with operation '"
00095           + oper->getName() + "'");
00096   }
00097 
00098   // Check if a flow with 1) identical buffer, 2) identical operation and
00099   // 3) overlapping effectivity dates already exists
00100   Operation::flowlist::const_iterator i = oper->getFlows().begin();
00101   for (; i != oper->getFlows().end(); ++i)
00102     if (i->getBuffer() == buf
00103         && i->getEffective().overlap(getEffective())
00104         && &*i != this)
00105       break;
00106 
00107   // Apply the appropriate action
00108   switch (action)
00109   {
00110     case ADD:
00111       if (i != oper->getFlows().end())
00112         throw DataException("Flow of '" + oper->getName() + "' and '" +
00113             buf->getName() + "' already exists");
00114       break;
00115     case CHANGE:
00116       throw DataException("Can't update a flow");
00117     case ADD_CHANGE:
00118       // ADD is handled in the code after the switch statement
00119       if (i == oper->getFlows().end()) break;
00120       throw DataException("Can't update a flow");
00121     case REMOVE:
00122       // Delete the temporary flow object
00123       delete this;
00124       // Nothing to delete
00125       if (i == oper->getFlows().end())
00126         throw DataException("Can't remove nonexistent flow of '"
00127             + oper->getName() + "' and '" + buf->getName() + "'");
00128       // Delete
00129       delete &*i;
00130   }
00131 
00132   // Attach to buffers higher up in the hierarchy
00133   // Note that the owner can create more loads if it has an owner too.
00134   if (buf->hasOwner() && action!=REMOVE) new Flow(oper, buf->getOwner(), quantity);
00135 
00136   // Set a flag to make sure the level computation is triggered again
00137   HasLevel::triggerLazyRecomputation();
00138 }
00139 
00140 
00141 DECLARE_EXPORT Flow::~Flow()
00142 {
00143   // Set a flag to make sure the level computation is triggered again
00144   HasLevel::triggerLazyRecomputation();
00145 
00146   // Delete existing flowplans
00147   if (getOperation() && getBuffer())
00148   {
00149     // Loop over operationplans
00150     for(OperationPlan::iterator i(getOperation()); i != OperationPlan::end(); ++i)
00151       // Loop over flowplans
00152       for(OperationPlan::FlowPlanIterator j = i->beginFlowPlans(); j != i->endFlowPlans(); )
00153         if (j->getFlow() == this) j.deleteFlowPlan();
00154         else ++j;
00155   }
00156 
00157   // Delete the flow from the operation and the buffer
00158   if (getOperation()) getOperation()->flowdata.erase(this);
00159   if (getBuffer()) getBuffer()->flows.erase(this);
00160 
00161   // Clean up alternate flows
00162   if (hasAlts)
00163   {
00164     // The flow has alternates.
00165     // Make a new flow the leading one. Or if there is only one alternate
00166     // present it is not marked as an alternate any more.
00167     unsigned short cnt = 0;
00168     int minprio = INT_MAX;
00169     Flow* newLeader = NULL;
00170     for (Operation::flowlist::iterator i = getOperation()->flowdata.begin();
00171         i != getOperation()->flowdata.end(); ++i)
00172       if (i->altFlow == this)
00173       {
00174         cnt++;
00175         if (i->priority < minprio)
00176         {
00177           newLeader = &*i;
00178           minprio = i->priority;
00179         }
00180       }
00181     if (cnt < 1)
00182       throw LogicException("Alternate flows update failure");
00183     else if (cnt == 1)
00184       // No longer an alternate any more
00185       newLeader->altFlow = NULL;
00186     else
00187     {
00188       // Mark a new leader flow
00189       newLeader->hasAlts = true;
00190       newLeader->altFlow = NULL;
00191       for (Operation::flowlist::iterator i = getOperation()->flowdata.begin();
00192           i != getOperation()->flowdata.end(); ++i)
00193         if (i->altFlow == this) i->altFlow = newLeader;
00194     }
00195   }
00196   if (altFlow)
00197   {
00198     // The flow is an alternate of another one.
00199     // If it was the only alternate, then the hasAlts flag on the parent
00200     // flow needs to be set back to false
00201     bool only_one = true;
00202     for (Operation::flowlist::iterator i = getOperation()->flowdata.begin();
00203         i != getOperation()->flowdata.end(); ++i)
00204       if (i->altFlow == altFlow)
00205       {
00206         only_one = false;
00207         break;
00208       }
00209     if (only_one) altFlow->hasAlts = false;
00210   }
00211 }
00212 
00213 
00214 DECLARE_EXPORT void Flow::setAlternate(Flow *f)
00215 {
00216   // Validate the argument
00217   if (!f)
00218     throw DataException("Setting NULL alternate flow");
00219   if (hasAlts || f->altFlow)
00220     throw DataException("Nested alternate flows are not allowed");
00221   if (!f->isConsumer() || !isConsumer())
00222     throw DataException("Only consuming alternate flows are supported");
00223 
00224   // Update both flows
00225   f->hasAlts = true;
00226   altFlow = f;
00227 }
00228 
00229 
00230 DECLARE_EXPORT void Flow::setAlternate(const string& n)
00231 {
00232   if (!getOperation())
00233     throw LogicException("Can't set an alternate flow before setting the operation");
00234   Flow *x = getOperation()->flowdata.find(n);
00235   if (!x) throw DataException("Can't find flow with name '" + n + "'");
00236   setAlternate(x);
00237 }
00238 
00239 
00240 DECLARE_EXPORT void Flow::writeElement (XMLOutput *o, const Keyword& tag, mode m) const
00241 {
00242   // If the flow has already been saved, no need to repeat it again
00243   // A 'reference' to a flow is not useful to be saved.
00244   if (m == REFERENCE) return;
00245   assert(m != NOHEADER);
00246 
00247   // Write the header
00248   o->BeginObject(tag, Tags::tag_type, getType().type);
00249 
00250   // If the flow is defined inside of an operation tag, we don't need to save
00251   // the operation. Otherwise we do save it...
00252   if (!dynamic_cast<Operation*>(o->getPreviousObject()))
00253     o->writeElement(Tags::tag_operation, getOperation());
00254 
00255   // If the flow is defined inside of an buffer tag, we don't need to save
00256   // the buffer. Otherwise we do save it...
00257   if (!dynamic_cast<Buffer*>(o->getPreviousObject()))
00258     o->writeElement(Tags::tag_buffer, getBuffer());
00259 
00260   // Write the quantity, priority, name and alternate
00261   o->writeElement(Tags::tag_quantity, getQuantity());
00262   if (getPriority()!=1) o->writeElement(Tags::tag_priority, getPriority());
00263   if (!getName().empty()) o->writeElement(Tags::tag_name, getName());
00264   if (getAlternate())
00265     o->writeElement(Tags::tag_alternate, getAlternate()->getName());
00266 
00267   // Write the effective daterange
00268   if (getEffective().getStart() != Date::infinitePast)
00269     o->writeElement(Tags::tag_effective_start, getEffective().getStart());
00270   if (getEffective().getEnd() != Date::infiniteFuture)
00271     o->writeElement(Tags::tag_effective_end, getEffective().getEnd());
00272 
00273   // End of flow object
00274   o->EndObject(tag);
00275 }
00276 
00277 
00278 DECLARE_EXPORT void Flow::beginElement(XMLInput& pIn, const Attribute& pAttr)
00279 {
00280   if (pAttr.isA (Tags::tag_buffer))
00281     pIn.readto( Buffer::reader(Buffer::metadata,pIn.getAttributes()) );
00282   else if (pAttr.isA (Tags::tag_operation))
00283     pIn.readto( Operation::reader(Operation::metadata,pIn.getAttributes()) );
00284 }
00285 
00286 
00287 DECLARE_EXPORT void Flow::endElement (XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement)
00288 {
00289   if (pAttr.isA (Tags::tag_buffer))
00290   {
00291     Buffer * b = dynamic_cast<Buffer*>(pIn.getPreviousObject());
00292     if (b) setBuffer(b);
00293     else throw LogicException("Incorrect object type during read operation");
00294   }
00295   else if (pAttr.isA (Tags::tag_operation))
00296   {
00297     Operation * o = dynamic_cast<Operation*>(pIn.getPreviousObject());
00298     if (o) setOperation(o);
00299     else throw LogicException("Incorrect object type during read operation");
00300   }
00301   else if (pAttr.isA(Tags::tag_quantity))
00302     setQuantity(pElement.getDouble());
00303   else if (pAttr.isA(Tags::tag_priority))
00304     setPriority(pElement.getInt());
00305   else if (pAttr.isA(Tags::tag_name))
00306     setName(pElement.getString());
00307   else if (pAttr.isA(Tags::tag_alternate))
00308     setAlternate(pElement.getString());
00309   else if (pAttr.isA(Tags::tag_search))
00310     setSearch(pElement.getString());
00311   else if (pAttr.isA(Tags::tag_action))
00312   {
00313     delete static_cast<Action*>(pIn.getUserArea());
00314     pIn.setUserArea(
00315       new Action(MetaClass::decodeAction(pElement.getString().c_str()))
00316     );
00317   }
00318   else if (pAttr.isA(Tags::tag_effective_end))
00319     setEffectiveEnd(pElement.getDate());
00320   else if (pAttr.isA(Tags::tag_effective_start))
00321     setEffectiveStart(pElement.getDate());
00322   else if (pIn.isObjectEnd())
00323   {
00324     // The flow data are now all read in. See if it makes sense now...
00325     Action a = pIn.getUserArea() ?
00326         *static_cast<Action*>(pIn.getUserArea()) :
00327         ADD_CHANGE;
00328     delete static_cast<Action*>(pIn.getUserArea());
00329     try { validate(a); }
00330     catch (...)
00331     {
00332       delete this;
00333       throw;
00334     }
00335   }
00336 }
00337 
00338 
00339 DECLARE_EXPORT void FlowEnd::writeElement
00340 (XMLOutput *o, const Keyword& tag, mode m) const
00341 {
00342   // If the flow has already been saved, no need to repeat it again
00343   // A 'reference' to a flow is not useful to be saved.
00344   if (m == REFERENCE) return;
00345   assert(m != NOHEADER);
00346 
00347   // Write the header
00348   o->BeginObject(tag, Tags::tag_type, getType().type);
00349 
00350   // If the flow is defined inside of an operation tag, we don't need to save
00351   // the operation. Otherwise we do save it...
00352   if (!dynamic_cast<Operation*>(o->getPreviousObject()))
00353     o->writeElement(Tags::tag_operation, getOperation());
00354 
00355   // If the flow is defined inside of an buffer tag, we don't need to save
00356   // the buffer. Otherwise we do save it...
00357   if (!dynamic_cast<Buffer*>(o->getPreviousObject()))
00358     o->writeElement(Tags::tag_buffer, getBuffer());
00359 
00360   // Write the quantity, priority name and alternate
00361   o->writeElement(Tags::tag_quantity, getQuantity());
00362   if (getPriority()!=1) o->writeElement(Tags::tag_priority, getPriority());
00363   if (!getName().empty()) o->writeElement(Tags::tag_name, getName());
00364   if (getAlternate())
00365     o->writeElement(Tags::tag_alternate, getAlternate()->getName());
00366 
00367   // Write the effective daterange
00368   if (getEffective().getStart() != Date::infinitePast)
00369     o->writeElement(Tags::tag_effective_start, getEffective().getStart());
00370   if (getEffective().getEnd() != Date::infiniteFuture)
00371     o->writeElement(Tags::tag_effective_end, getEffective().getEnd());
00372 
00373   // End of flow object
00374   o->EndObject(tag);
00375 }
00376 
00377 
00378 DECLARE_EXPORT PyObject* Flow::getattro(const Attribute& attr)
00379 {
00380   if (attr.isA(Tags::tag_buffer))
00381     return PythonObject(getBuffer());
00382   if (attr.isA(Tags::tag_operation))
00383     return PythonObject(getOperation());
00384   if (attr.isA(Tags::tag_quantity))
00385     return PythonObject(getQuantity());
00386   if (attr.isA(Tags::tag_priority))
00387     return PythonObject(getPriority());
00388   if (attr.isA(Tags::tag_effective_end))
00389     return PythonObject(getEffective().getEnd());
00390   if (attr.isA(Tags::tag_effective_start))
00391     return PythonObject(getEffective().getStart());
00392   if (attr.isA(Tags::tag_name))
00393     return PythonObject(getName());
00394   if (attr.isA(Tags::tag_hidden))
00395     return PythonObject(getHidden());
00396   if (attr.isA(Tags::tag_alternate))
00397     return PythonObject(getAlternate());
00398   if (attr.isA(Tags::tag_search))
00399   {
00400     ostringstream ch;
00401     ch << getSearch();
00402     return PythonObject(ch.str());
00403   }
00404   return NULL;
00405 }
00406 
00407 
00408 DECLARE_EXPORT int Flow::setattro(const Attribute& attr, const PythonObject& field)
00409 {
00410   if (attr.isA(Tags::tag_buffer))
00411   {
00412     if (!field.check(Buffer::metadata))
00413     {
00414       PyErr_SetString(PythonDataException, "flow buffer must be of type buffer");
00415       return -1;
00416     }
00417     Buffer* y = static_cast<Buffer*>(static_cast<PyObject*>(field));
00418     setBuffer(y);
00419   }
00420   else if (attr.isA(Tags::tag_operation))
00421   {
00422     if (!field.check(Operation::metadata))
00423     {
00424       PyErr_SetString(PythonDataException, "flow operation must be of type operation");
00425       return -1;
00426     }
00427     Operation* y = static_cast<Operation*>(static_cast<PyObject*>(field));
00428     setOperation(y);
00429   }
00430   else if (attr.isA(Tags::tag_quantity))
00431     setQuantity(field.getDouble());
00432   else if (attr.isA(Tags::tag_priority))
00433     setPriority(field.getInt());
00434   else if (attr.isA(Tags::tag_effective_end))
00435     setEffectiveEnd(field.getDate());
00436   else if (attr.isA(Tags::tag_effective_start))
00437     setEffectiveStart(field.getDate());
00438   else if (attr.isA(Tags::tag_name))
00439     setName(field.getString());
00440   else if (attr.isA(Tags::tag_alternate))
00441   {
00442     if (!field.check(Flow::metadata))
00443       setAlternate(field.getString());
00444     else
00445     {
00446       Flow *y = static_cast<Flow*>(static_cast<PyObject*>(field));
00447       setAlternate(y);
00448     }
00449   }
00450   else if (attr.isA(Tags::tag_search))
00451     setSearch(field.getString());
00452   else
00453     return -1;
00454   return 0;
00455 }
00456 
00457 
00458 /** @todo method implementation not generic and doesn't support clean subclassing. */
00459 PyObject* Flow::create(PyTypeObject* pytype, PyObject* args, PyObject* kwds)
00460 {
00461   try
00462   {
00463     // Pick up the operation
00464     PyObject* oper = PyDict_GetItemString(kwds,"operation");
00465     if (!PyObject_TypeCheck(oper, Operation::metadata->pythonClass))
00466       throw DataException("flow operation must be of type operation");
00467 
00468     // Pick up the resource
00469     PyObject* buf = PyDict_GetItemString(kwds,"buffer");
00470     if (!PyObject_TypeCheck(buf, Buffer::metadata->pythonClass))
00471       throw DataException("flow buffer must be of type buffer");
00472 
00473     // Pick up the quantity
00474     PyObject* q1 = PyDict_GetItemString(kwds,"quantity");
00475     double q2 = q1 ? PythonObject(q1).getDouble() : 1.0;
00476 
00477     // Pick up the effectivity dates
00478     DateRange eff;
00479     PyObject* eff_start = PyDict_GetItemString(kwds,"effective_start");
00480     if (eff_start)
00481     {
00482       PythonObject d(eff_start);
00483       eff.setStart(d.getDate());
00484     }
00485     PyObject* eff_end = PyDict_GetItemString(kwds,"effective_end");
00486     if (eff_end)
00487     {
00488       PythonObject d(eff_end);
00489       eff.setEnd(d.getDate());
00490     }
00491 
00492     // Pick up the type and create the flow
00493     Flow *l;
00494     PyObject* t = PyDict_GetItemString(kwds,"type");
00495     if (t)
00496     {
00497       PythonObject d(t);
00498       if (d.getString() == "flow_end")
00499         l = new FlowEnd(
00500           static_cast<Operation*>(oper),
00501           static_cast<Buffer*>(buf),
00502           q2, eff
00503         );
00504       else
00505         l = new FlowStart(
00506           static_cast<Operation*>(oper),
00507           static_cast<Buffer*>(buf),
00508           q2, eff
00509         );
00510     }
00511     else
00512       l = new FlowStart(
00513         static_cast<Operation*>(oper),
00514         static_cast<Buffer*>(buf),
00515         q2, eff
00516       );
00517 
00518     // Return the object
00519     Py_INCREF(l);
00520     return static_cast<PyObject*>(l);
00521   }
00522   catch (...)
00523   {
00524     PythonType::evalException();
00525     return NULL;
00526   }
00527 }
00528 
00529 
00530 int FlowIterator::initialize()
00531 {
00532   // Initialize the type
00533   PythonType& x = PythonExtension<FlowIterator>::getType();
00534   x.setName("flowIterator");
00535   x.setDoc("frePPLe iterator for flows");
00536   x.supportiter();
00537   return x.typeReady();
00538 }
00539 
00540 
00541 PyObject* FlowIterator::iternext()
00542 {
00543   PyObject* result;
00544   if (buf)
00545   {
00546     // Iterate over flows on a buffer
00547     if (ib == buf->getFlows().end()) return NULL;
00548     result = const_cast<Flow*>(&*ib);
00549     ++ib;
00550   }
00551   else
00552   {
00553     // Iterate over flows on an operation
00554     if (io == oper->getFlows().end()) return NULL;
00555     result = const_cast<Flow*>(&*io);
00556     ++io;
00557   }
00558   Py_INCREF(result);
00559   return result;
00560 }
00561 
00562 } // end namespace

Documentation generated for frePPLe by  doxygen