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