problem.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002   file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/tags/0.8.0/src/model/problem.cpp $
00003   version : $LastChangedRevision: 1176 $  $LastChangedBy: jdetaeye $
00004   date : $LastChangedDate: 2010-02-14 22:27:13 +0100 (Sun, 14 Feb 2010) $
00005  ***************************************************************************/
00006 
00007 /***************************************************************************
00008  *                                                                         *
00009  * Copyright (C) 2007 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 
00031 namespace frepple
00032 {
00033 
00034 DECLARE_EXPORT bool Plannable::anyChange = false;
00035 DECLARE_EXPORT bool Plannable::computationBusy = false;
00036 DECLARE_EXPORT const MetaCategory* Problem::metadata;
00037 DECLARE_EXPORT const MetaClass* ProblemMaterialExcess::metadata,
00038   *ProblemMaterialShortage::metadata,
00039   *ProblemExcess::metadata,
00040   *ProblemShort::metadata,
00041   *ProblemEarly::metadata,
00042   *ProblemLate::metadata,
00043   *ProblemDemandNotPlanned::metadata,
00044   *ProblemPrecedence::metadata,
00045   *ProblemBeforeFence::metadata,
00046   *ProblemBeforeCurrent::metadata,
00047   *ProblemCapacityUnderload::metadata,
00048   *ProblemCapacityOverload::metadata;
00049 
00050 
00051 int Problem::initialize()
00052 {
00053   // Initialize the problem metadata.
00054   Problem::metadata = new MetaCategory
00055     ("problem", "problems", NULL, Problem::writer);
00056   ProblemMaterialExcess::metadata = new MetaClass
00057     ("problem","material excess");
00058   ProblemMaterialShortage::metadata = new MetaClass
00059     ("problem","material shortage");
00060   ProblemExcess::metadata = new MetaClass
00061     ("problem","excess");
00062   ProblemShort::metadata = new MetaClass
00063     ("problem","short");
00064   ProblemEarly::metadata = new MetaClass
00065     ("problem","early");
00066   ProblemLate::metadata = new MetaClass
00067     ("problem","late");
00068   ProblemDemandNotPlanned::metadata = new MetaClass
00069     ("problem","unplanned");
00070   ProblemPrecedence::metadata = new MetaClass
00071     ("problem","precedence");
00072   ProblemBeforeFence::metadata = new MetaClass
00073     ("problem","before fence");
00074   ProblemBeforeCurrent::metadata = new MetaClass
00075     ("problem","before current");
00076   ProblemCapacityUnderload::metadata = new MetaClass
00077     ("problem","underload");
00078   ProblemCapacityOverload::metadata = new MetaClass
00079     ("problem","overload");
00080 
00081   // Initialize the Python type
00082   PythonType& x = PythonExtension<Problem>::getType();
00083   x.setName("problem");
00084   x.setDoc("frePPLe problem");
00085   x.supportgetattro();
00086   x.supportstr();
00087   x.addMethod("toXML", toXML, METH_VARARGS, "return a XML representation");
00088   const_cast<MetaCategory*>(metadata)->pythonClass = x.type_object();
00089   return x.typeReady();
00090 }
00091 
00092 
00093 DECLARE_EXPORT bool Problem::operator < (const Problem& a) const
00094 {
00095   // 1. Sort based on entity
00096   assert(owner == a.owner);
00097 
00098   // 2. Sort based on type
00099   if (getType() != a.getType()) return getType() < a.getType();
00100 
00101   // 3. Sort based on start date
00102   return getDateRange().getStart() < a.getDateRange().getStart();
00103 }
00104 
00105 
00106 DECLARE_EXPORT void Problem::addProblem()
00107 {
00108   assert(owner);
00109   if ((owner->firstProblem && *this < *(owner->firstProblem))
00110       || !owner->firstProblem)
00111   {
00112     // Insert as the first problem in the list
00113     nextProblem = owner->firstProblem;
00114     owner->firstProblem = this;
00115   }
00116   else
00117   {
00118     // Insert in the middle or at the end of the list
00119     Problem* curProblem = owner->firstProblem->nextProblem;
00120     Problem* prevProblem = owner->firstProblem;
00121     while (curProblem && !(*this < *curProblem))
00122     {
00123       prevProblem = curProblem;
00124       curProblem = curProblem->nextProblem;
00125     }
00126     nextProblem = curProblem;
00127     prevProblem->nextProblem = this;
00128   }
00129 }
00130 
00131 
00132 DECLARE_EXPORT void Problem::removeProblem()
00133 {
00134   // Fast delete method: the code triggering this method is responsible of
00135   // maintaining the problem container
00136   if (!owner) return;
00137 
00138   if (owner->firstProblem == this)
00139     // Removal from the head of the list
00140     owner->firstProblem = nextProblem;
00141   else
00142   {
00143     // Removal from the middle of the list
00144     Problem *prev = owner->firstProblem;
00145     for (Problem* cur = owner->firstProblem; cur; cur=cur->nextProblem)
00146     {
00147       if (cur == this)
00148       {
00149         // Found it!
00150         prev->nextProblem = nextProblem;
00151         return;
00152       }
00153       prev = cur;
00154     }
00155     // The problem wasn't found in the list. This shouldn't happen...
00156     throw LogicException("Corrupted problem list");
00157   }
00158 }
00159 
00160 
00161 DECLARE_EXPORT void Plannable::setDetectProblems(bool b)
00162 {
00163   if (useProblemDetection && !b)
00164     // We are switching from 'yes' to 'no': delete all existing problems
00165     Problem::clearProblems(*this);
00166   else if (!useProblemDetection && b)
00167     // We are switching from 'no' to 'yes': mark as changed for the next
00168     // problem detection call
00169     setChanged();
00170   // Update the flag
00171   useProblemDetection=b;
00172 }
00173 
00174 
00175 DECLARE_EXPORT void Plannable::computeProblems()
00176 {
00177   // Exit immediately if the list is up to date
00178   if (!anyChange && !computationBusy) return;
00179 
00180   computationBusy = true;
00181   // Get exclusive access to this function in a multi-threaded environment.
00182   static Mutex computationbusy;
00183   {
00184     ScopeMutexLock l(computationbusy);
00185 
00186     // Another thread may already have computed it while this thread was
00187     // waiting for the lock
00188     while (anyChange)
00189     {
00190       // Reset to change flag. Note that during the computation the flag
00191       // could be switched on again by some model change in a different thread.
00192       anyChange = false;
00193 
00194       // Loop through all entities
00195       for (HasProblems::EntityIterator i; i!=HasProblems::endEntity(); ++i)
00196       {
00197         Plannable *e = i->getEntity();
00198         if (e->getChanged() && e->getDetectProblems()) i->updateProblems();
00199       }
00200 
00201       // Mark the entities as unchanged
00202       for (HasProblems::EntityIterator j; j!=HasProblems::endEntity(); ++j)
00203       {
00204         Plannable *e = j->getEntity();
00205         if (e->getChanged() && e->getDetectProblems()) e->setChanged(false);
00206       }
00207     }
00208 
00209     // Unlock the exclusive access to this function
00210     computationBusy = false;
00211   }
00212 }
00213 
00214 
00215 DECLARE_EXPORT void Plannable::writeElement (XMLOutput* o, const Keyword& tag, mode m) const
00216 {
00217   // We don't bother about the mode, since this method is only called from
00218   // within the writeElement() method of other classes.
00219 
00220   // Problem detection flag only written if different from the default value
00221   if (!getDetectProblems()) o->writeElement(Tags::tag_detectproblems, false);
00222 }
00223 
00224 
00225 DECLARE_EXPORT void Plannable::endElement(XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement)
00226 {
00227   if (pAttr.isA (Tags::tag_detectproblems))
00228   {
00229     bool b = pElement.getBool();
00230     setDetectProblems(b);
00231   }
00232 }
00233 
00234 
00235 DECLARE_EXPORT void Problem::clearProblems()
00236 {
00237   // Loop through all entities, and call clearProblems(i)
00238   for (HasProblems::EntityIterator i = HasProblems::beginEntity();
00239       i != HasProblems::endEntity(); ++i)
00240   {
00241     clearProblems(*i);
00242     i->getEntity()->setChanged(true);
00243   }
00244 }
00245 
00246 
00247 DECLARE_EXPORT void Problem::clearProblems(HasProblems& p, bool setchanged)
00248 {
00249   // Nothing to do
00250   if (!p.firstProblem) return;
00251 
00252   // Delete all problems in the list
00253   for (Problem *cur=p.firstProblem; cur; )
00254   {
00255     Problem *del = cur;
00256     cur = cur->nextProblem;
00257     del->owner = NULL;
00258     delete del;
00259   }
00260   p.firstProblem = NULL;
00261 
00262   // Mark as changed
00263   if (setchanged) p.getEntity()->setChanged();
00264 }
00265 
00266 
00267 DECLARE_EXPORT void Problem::writer(const MetaCategory* c, XMLOutput* o)
00268 {
00269   const_iterator piter = begin();
00270   if (piter != end())
00271   {
00272     o->BeginObject(*c->grouptag);
00273     for (; piter!=end(); ++piter)
00274       // Note: not the regular write, but a fast write to speed things up.
00275       // This is possible since problems aren't nested and are never
00276       // referenced.
00277       piter->writeElement(o, *c->typetag);
00278     o->EndObject(*c->grouptag);
00279   }
00280 }
00281 
00282 
00283 DECLARE_EXPORT void Problem::writeElement(XMLOutput *o, const Keyword& tag, mode m) const
00284 {
00285   // We ignore the mode, and always write the complete model
00286   o->BeginObject(tag);
00287   o->writeElement(Tags::tag_name, getType().type);
00288   o->writeElement(Tags::tag_description, getDescription());
00289   o->writeElement(Tags::tag_start, getDateRange().getStart());
00290   o->writeElement(Tags::tag_end, getDateRange().getEnd());
00291   o->writeElement(Tags::tag_weight, getWeight());
00292   o->EndObject(tag);
00293 }
00294 
00295 
00296 DECLARE_EXPORT HasProblems::EntityIterator::EntityIterator() : type(0)
00297 {
00298   // Buffer
00299   bufIter = new Buffer::iterator(Buffer::begin());
00300   if (*bufIter != Buffer::end()) return;
00301 
00302   // Move on to resource if there are no buffers
00303   delete bufIter;
00304   type = 1;
00305   resIter = new Resource::iterator(Resource::begin());
00306   if (*resIter != Resource::end()) return;
00307 
00308   // Move on to operationplans if there are no resources either
00309   delete resIter;
00310   type = 2;
00311   operIter = new OperationPlan::iterator(OperationPlan::begin());
00312   if (*operIter != OperationPlan::end()) return;
00313 
00314   // Move on to demands if there are no operationplans either
00315   delete operIter;
00316   type = 3;
00317   demIter = new Demand::iterator(Demand::begin());
00318   if (*demIter == Demand::end())
00319   {
00320     // There is nothing at all in this model
00321     delete demIter;
00322     type = 4;
00323   }
00324 }
00325 
00326 
00327 DECLARE_EXPORT HasProblems::EntityIterator& HasProblems::EntityIterator::operator++()
00328 {
00329   switch (type)
00330   {
00331     case 0:
00332       // Buffer
00333       if (*bufIter != Buffer::end())
00334         if (++(*bufIter) != Buffer::end()) return *this;
00335       ++type;
00336       delete bufIter;
00337       resIter = new Resource::iterator(Resource::begin());
00338       if (*resIter != Resource::end()) return *this;
00339       // Note: no break statement
00340     case 1:
00341       // Resource
00342       if (*resIter != Resource::end())
00343         if (++(*resIter) != Resource::end()) return *this;
00344       ++type;
00345       delete resIter;
00346       operIter = new OperationPlan::iterator(OperationPlan::begin());
00347       if (*operIter != OperationPlan::end()) return *this;
00348       // Note: no break statement
00349     case 2:
00350       // Operationplan
00351       if (*operIter != OperationPlan::end())
00352         if (++(*operIter) != OperationPlan::end()) return *this;
00353       ++type;
00354       delete operIter;
00355       demIter = new Demand::iterator(Demand::begin());
00356       if (*demIter != Demand::end()) return *this;
00357       // Note: no break statement
00358     case 3:
00359       // Demand
00360       if (*demIter != Demand::end())
00361         if (++(*demIter) != Demand::end()) return *this;
00362       // Ended recursing of all entities
00363       ++type;
00364       delete demIter;
00365       demIter = NULL;
00366       return *this;
00367   }
00368   throw LogicException("Unreachable code reached");
00369 }
00370 
00371 
00372 DECLARE_EXPORT HasProblems::EntityIterator::~EntityIterator()
00373 {
00374   switch (type)
00375   {
00376     // Buffer
00377     case 0: delete bufIter; return;
00378     // Resource
00379     case 1: delete resIter; return;
00380     // Operation
00381     case 2: delete operIter; return;
00382     // Demand
00383     case 3: delete demIter; return;
00384   }
00385 }
00386 
00387 
00388 DECLARE_EXPORT HasProblems::EntityIterator::EntityIterator(const EntityIterator& o)
00389 {
00390   // Delete old iterator
00391   this->~EntityIterator();
00392   // Populate new values
00393   type = o.type;
00394   if (type==0) bufIter = new Buffer::iterator(*(o.bufIter));
00395   else if (type==1) resIter = new Resource::iterator(*(o.resIter));
00396   else if (type==2) operIter = new OperationPlan::iterator(*(o.operIter));
00397   else if (type==3) demIter = new Demand::iterator(*(o.demIter));
00398 }
00399 
00400 
00401 DECLARE_EXPORT HasProblems::EntityIterator&
00402 HasProblems::EntityIterator::operator=(const EntityIterator& o)
00403 {
00404   // Gracefully handle self assignment
00405   if (this == &o) return *this;
00406   // Delete old iterator
00407   this->~EntityIterator();
00408   // Populate new values
00409   type = o.type;
00410   if (type==0) bufIter = new Buffer::iterator(*(o.bufIter));
00411   else if (type==1) resIter = new Resource::iterator(*(o.resIter));
00412   else if (type==2) operIter = new OperationPlan::iterator(*(o.operIter));
00413   else if (type==3) demIter = new Demand::iterator(*(o.demIter));
00414   return *this;
00415 }
00416 
00417 
00418 DECLARE_EXPORT bool
00419 HasProblems::EntityIterator::operator != (const EntityIterator& t) const
00420 {
00421   // Different iterator type, thus always different and return false
00422   if (type != t.type) return true;
00423 
00424   // Same iterator type, more granular comparison required
00425   switch (type)
00426   {
00427     // Buffer
00428     case 0: return *bufIter != *(t.bufIter);
00429     // Resource
00430     case 1: return *resIter != *(t.resIter);
00431     // Operationplan
00432     case 2: return *operIter != *(t.operIter);
00433     // Demand
00434     case 3: return *demIter != *(t.demIter);
00435     // Always return true for higher type numbers. This should happen only
00436     // when comparing with the end of list element.
00437     default: return false;
00438   }
00439 }
00440 
00441 
00442 DECLARE_EXPORT HasProblems& HasProblems::EntityIterator::operator*() const
00443 {
00444   switch (type)
00445   {
00446     // Buffer
00447     case 0: return **bufIter;
00448     // Resource
00449     case 1: return **resIter;
00450     // Operation
00451     case 2: return **operIter;
00452     // Demand
00453     case 3: return **demIter;
00454     default: throw LogicException("Unreachable code reached");
00455   }
00456 }
00457 
00458 
00459 DECLARE_EXPORT HasProblems* HasProblems::EntityIterator::operator->() const
00460 {
00461   switch (type)
00462   {
00463     // Buffer
00464     case 0: return &**bufIter;
00465     // Resource
00466     case 1: return &**resIter;
00467     // Operationplan
00468     case 2: return &**operIter;
00469     // Demand
00470     case 3: return &**demIter;
00471     default: throw LogicException("Unreachable code reached");
00472   }
00473 }
00474 
00475 
00476 DECLARE_EXPORT HasProblems::EntityIterator HasProblems::beginEntity()
00477 {
00478   return EntityIterator();
00479 }
00480 
00481 
00482 DECLARE_EXPORT HasProblems::EntityIterator HasProblems::endEntity()
00483 {
00484   // Note that we give call a constructor with type 4, in order to allow
00485   // a fast comparison.
00486   return EntityIterator(4);
00487 }
00488 
00489 
00490 DECLARE_EXPORT Problem::const_iterator& Problem::const_iterator::operator++()
00491 {
00492   // Incrementing beyond the end
00493   if (!iter) return *this;
00494 
00495   // Move to the next problem
00496   iter = iter->nextProblem;
00497 
00498   // Move to the next entity
00499   // We need a while loop here because some entities can be without problems
00500   while (!iter && !owner && eiter!=HasProblems::endEntity())
00501   {
00502     ++eiter;
00503     if (eiter!=HasProblems::endEntity()) iter = eiter->firstProblem;
00504   }
00505   return *this;
00506 }
00507 
00508 
00509 DECLARE_EXPORT Problem::const_iterator Problem::begin()
00510 {
00511   Plannable::computeProblems();
00512   return const_iterator();
00513 }
00514 
00515 
00516 DECLARE_EXPORT Problem::const_iterator Problem::begin(HasProblems* i, bool refresh)
00517 {
00518   // Null pointer passed, loop through the full list anyway
00519   if (!i) return begin();
00520 
00521   // Return an iterator for a single entity
00522   if (refresh) i->updateProblems();
00523   return const_iterator(i);
00524 }
00525 
00526 
00527 DECLARE_EXPORT const Problem::const_iterator Problem::end()
00528 {
00529   return const_iterator(NULL);
00530 }
00531 
00532 
00533 PyObject* Problem::getattro(const Attribute& attr)
00534 {
00535   if (attr.isA(Tags::tag_name))
00536     return PythonObject(getType().type);
00537   if (attr.isA(Tags::tag_description))
00538     return PythonObject(getDescription());
00539   if (attr.isA(Tags::tag_entity))
00540     return PythonObject(getEntity());
00541   if (attr.isA(Tags::tag_start))
00542     return PythonObject(getDateRange().getStart());
00543   if (attr.isA(Tags::tag_end))
00544     return PythonObject(getDateRange().getEnd());
00545   if (attr.isA(Tags::tag_weight))
00546     return PythonObject(getWeight());
00547   if (attr.isA(Tags::tag_owner))
00548     return PythonObject(getOwner());
00549   return NULL;
00550 }
00551 
00552 } // End namespace