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

Documentation generated for frePPLe by  doxygen