load.cpp

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

Documentation generated for frePPLe by  doxygen