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

Documentation generated for frePPLe by  doxygen