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

Documentation generated for frePPLe by  doxygen