problem.cpp
Go to the documentation of this file.
00001 /*************************************************************************** 00002 file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/trunk/src/model/problem.cpp $ 00003 version : $LastChangedRevision: 1505 $ $LastChangedBy: jdetaeye $ 00004 date : $LastChangedDate: 2011-08-26 18:55:08 +0200 (Fri, 26 Aug 2011) $ 00005 ***************************************************************************/ 00006 00007 /*************************************************************************** 00008 * * 00009 * Copyright (C) 2007-2011 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