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

Documentation generated for frePPLe by  doxygen