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

Documentation generated for frePPLe by  doxygen