flow.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002   file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/trunk/src/model/flow.cpp $
00003   version : $LastChangedRevision: 1505 $  $LastChangedBy: jdetaeye $
00004   date : $LastChangedDate: 2011-08-26 18:55:08 +0200 (Fri, 26 Aug 2011) $
00005  ***************************************************************************/
00006 
00007 /***************************************************************************
00008  *                                                                         *
00009  * Copyright (C) 2007-2011 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_alternate))
00395     return PythonObject(getAlternate());
00396   if (attr.isA(Tags::tag_search))
00397   {
00398     ostringstream ch;
00399     ch << getSearch();
00400     return PythonObject(ch.str());
00401   }
00402   return NULL;
00403 }
00404 
00405 
00406 DECLARE_EXPORT int Flow::setattro(const Attribute& attr, const PythonObject& field)
00407 {
00408   if (attr.isA(Tags::tag_buffer))
00409   {
00410     if (!field.check(Buffer::metadata))
00411     {
00412       PyErr_SetString(PythonDataException, "flow buffer must be of type buffer");
00413       return -1;
00414     }
00415     Buffer* y = static_cast<Buffer*>(static_cast<PyObject*>(field));
00416     setBuffer(y);
00417   }
00418   else if (attr.isA(Tags::tag_operation))
00419   {
00420     if (!field.check(Operation::metadata))
00421     {
00422       PyErr_SetString(PythonDataException, "flow operation must be of type operation");
00423       return -1;
00424     }
00425     Operation* y = static_cast<Operation*>(static_cast<PyObject*>(field));
00426     setOperation(y);
00427   }
00428   else if (attr.isA(Tags::tag_quantity))
00429     setQuantity(field.getDouble());
00430   else if (attr.isA(Tags::tag_priority))
00431     setPriority(field.getInt());
00432   else if (attr.isA(Tags::tag_effective_end))
00433     setEffectiveEnd(field.getDate());
00434   else if (attr.isA(Tags::tag_effective_start))
00435     setEffectiveStart(field.getDate());
00436   else if (attr.isA(Tags::tag_name))
00437     setName(field.getString());
00438   else if (attr.isA(Tags::tag_alternate))
00439   {
00440     if (!field.check(Flow::metadata))
00441       setAlternate(field.getString());
00442     else
00443     {
00444       Flow *y = static_cast<Flow*>(static_cast<PyObject*>(field));
00445       setAlternate(y);
00446     }
00447   }
00448   else if (attr.isA(Tags::tag_search))
00449     setSearch(field.getString());
00450   else
00451     return -1;
00452   return 0;
00453 }
00454 
00455 
00456 /** @todo method implementation not generic and doesn't support clean subclassing. */
00457 PyObject* Flow::create(PyTypeObject* pytype, PyObject* args, PyObject* kwds)
00458 {
00459   try
00460   {
00461     // Pick up the operation
00462     PyObject* oper = PyDict_GetItemString(kwds,"operation");
00463     if (!PyObject_TypeCheck(oper, Operation::metadata->pythonClass))
00464       throw DataException("flow operation must be of type operation");
00465 
00466     // Pick up the resource
00467     PyObject* buf = PyDict_GetItemString(kwds,"buffer");
00468     if (!PyObject_TypeCheck(buf, Buffer::metadata->pythonClass))
00469       throw DataException("flow buffer must be of type buffer");
00470 
00471     // Pick up the quantity
00472     PyObject* q1 = PyDict_GetItemString(kwds,"quantity");
00473     double q2 = q1 ? PythonObject(q1).getDouble() : 1.0;
00474 
00475     // Pick up the effectivity dates
00476     DateRange eff;
00477     PyObject* eff_start = PyDict_GetItemString(kwds,"effective_start");
00478     if (eff_start)
00479     {
00480       PythonObject d(eff_start);
00481       eff.setStart(d.getDate());
00482     }
00483     PyObject* eff_end = PyDict_GetItemString(kwds,"effective_end");
00484     if (eff_end)
00485     {
00486       PythonObject d(eff_end);
00487       eff.setEnd(d.getDate());
00488     }
00489 
00490     // Pick up the type and create the flow
00491     Flow *l;
00492     PyObject* t = PyDict_GetItemString(kwds,"type");
00493     if (t)
00494     {
00495       PythonObject d(t);
00496       if (d.getString() == "flow_end")
00497         l = new FlowEnd(
00498           static_cast<Operation*>(oper),
00499           static_cast<Buffer*>(buf),
00500           q2, eff
00501           );
00502       else
00503         l = new FlowStart(
00504           static_cast<Operation*>(oper),
00505           static_cast<Buffer*>(buf),
00506           q2, eff
00507           );
00508     }
00509     else
00510       l = new FlowStart(
00511         static_cast<Operation*>(oper),
00512         static_cast<Buffer*>(buf),
00513         q2, eff
00514         );
00515 
00516     // Return the object
00517     Py_INCREF(l);
00518     return static_cast<PyObject*>(l);
00519   }
00520   catch (...)
00521   {
00522     PythonType::evalException();
00523     return NULL;
00524   }
00525 }
00526 
00527 
00528 int FlowIterator::initialize()
00529 {
00530   // Initialize the type
00531   PythonType& x = PythonExtension<FlowIterator>::getType();
00532   x.setName("flowIterator");
00533   x.setDoc("frePPLe iterator for flows");
00534   x.supportiter();
00535   return x.typeReady();
00536 }
00537 
00538 
00539 PyObject* FlowIterator::iternext()
00540 {
00541   PyObject* result;
00542   if (buf)
00543   {
00544     // Iterate over flows on a buffer
00545     if (ib == buf->getFlows().end()) return NULL;
00546     result = const_cast<Flow*>(&*ib);
00547     ++ib;
00548   }
00549   else
00550   {
00551     // Iterate over flows on an operation
00552     if (io == oper->getFlows().end()) return NULL;
00553     result = const_cast<Flow*>(&*io);
00554     ++io;
00555   }
00556   Py_INCREF(result);
00557   return result;
00558 }
00559 
00560 } // end namespace

Documentation generated for frePPLe by  doxygen