operationplan.cpp
Go to the documentation of this file.00001 /*************************************************************************** 00002 file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/trunk/src/model/operationplan.cpp $ 00003 version : $LastChangedRevision: 1341 $ $LastChangedBy: jdetaeye $ 00004 date : $LastChangedDate: 2010-08-21 07:17:25 +0200 (Sat, 21 Aug 2010) $ 00005 ***************************************************************************/ 00006 00007 /*************************************************************************** 00008 * * 00009 * Copyright (C) 2007-2010 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 const MetaClass* OperationPlan::metadata; 00035 DECLARE_EXPORT const MetaCategory* OperationPlan::metacategory; 00036 DECLARE_EXPORT unsigned long OperationPlan::counterMin = 1; 00037 // The value of the max counter is hard-coded to 2^31 - 1. This value is the 00038 // highest positive integer number that can safely be used on 32-bit platforms. 00039 // An alternative approach is to use the value ULONG_MAX, but this has the 00040 // disadvantage of not being portable across platforms and tools. 00041 DECLARE_EXPORT unsigned long OperationPlan::counterMax = 2147483647; 00042 00043 00044 int OperationPlan::initialize() 00045 { 00046 // Initialize the metadata 00047 OperationPlan::metacategory = new MetaCategory("operationplan", "operationplans", 00048 OperationPlan::createOperationPlan, OperationPlan::writer); 00049 OperationPlan::metadata = new MetaClass("operationplan", "operationplan"); 00050 00051 // Initialize the Python type 00052 PythonType& x = FreppleCategory<OperationPlan>::getType(); 00053 x.setName("operationplan"); 00054 x.setDoc("frePPLe operationplan"); 00055 x.supportgetattro(); 00056 x.supportsetattro(); 00057 x.supportstr(); 00058 x.supportcreate(create); 00059 x.addMethod("toXML", toXML, METH_VARARGS, "return a XML representation"); 00060 const_cast<MetaClass*>(metadata)->pythonClass = x.type_object(); 00061 return x.typeReady(); 00062 } 00063 00064 00065 void DECLARE_EXPORT OperationPlan::setChanged(bool b) 00066 { 00067 if (owner) 00068 owner->setChanged(b); 00069 else 00070 { 00071 oper->setChanged(b); 00072 if (dmd) dmd->setChanged(); 00073 } 00074 } 00075 00076 00077 DECLARE_EXPORT Object* OperationPlan::createOperationPlan 00078 (const MetaClass* cat, const AttributeList& in) 00079 { 00080 // Pick up the action attribute 00081 Action action = MetaClass::decodeAction(in); 00082 00083 // Decode the attributes 00084 const DataElement* opnameElement = in.get(Tags::tag_operation); 00085 if (!*opnameElement && action!=REMOVE) 00086 throw DataException("Missing operation attribute"); 00087 string opname = *opnameElement ? opnameElement->getString() : ""; 00088 00089 // Decode the operationplan identifier 00090 unsigned long id = 0; 00091 const DataElement* idfier = in.get(Tags::tag_id); 00092 if (*idfier) id = idfier->getUnsignedLong(); 00093 00094 // If an ID is specified, we look up this operation plan 00095 OperationPlan* opplan = NULL; 00096 if (idfier) 00097 { 00098 opplan = OperationPlan::findId(id); 00099 if (opplan && !opname.empty() 00100 && opplan->getOperation()->getName()==opname) 00101 { 00102 // Previous and current operations don't match. 00103 ostringstream ch; 00104 ch << "Operationplan id " << id 00105 << " defined multiple times with different operations: '" 00106 << opplan->getOperation() << "' & '" << opname << "'"; 00107 throw DataException(ch.str()); 00108 } 00109 } 00110 00111 // Execute the proper action 00112 switch (action) 00113 { 00114 case REMOVE: 00115 if (opplan) 00116 { 00117 // Send out the notification to subscribers 00118 if (opplan->getType().raiseEvent(opplan, SIG_REMOVE)) 00119 // Delete it 00120 delete opplan; 00121 else 00122 { 00123 // The callbacks disallowed the deletion! 00124 ostringstream ch; 00125 ch << "Can't delete operationplan with id " << id; 00126 throw DataException(ch.str()); 00127 } 00128 } 00129 else 00130 { 00131 ostringstream ch; 00132 ch << "Can't find operationplan with identifier " 00133 << id << " for removal"; 00134 throw DataException(ch.str()); 00135 } 00136 return NULL; 00137 case ADD: 00138 if (opplan) 00139 { 00140 ostringstream ch; 00141 ch << "Operationplan with identifier " << id 00142 << " already exists and can't be added again"; 00143 throw DataException(ch.str()); 00144 } 00145 if (opname.empty()) 00146 throw DataException 00147 ("Operation name missing for creating an operationplan"); 00148 break; 00149 case CHANGE: 00150 if (!opplan) 00151 { 00152 ostringstream ch; 00153 ch << "Operationplan with identifier " << id << " doesn't exist"; 00154 throw DataException(ch.str()); 00155 } 00156 break; 00157 case ADD_CHANGE: ; 00158 } 00159 00160 // Return the existing operationplan 00161 if (opplan) return opplan; 00162 00163 // Create a new operation plan 00164 Operation* oper = Operation::find(opname); 00165 if (!oper) 00166 { 00167 // Can't create operationplan because the operation doesn't exist 00168 throw DataException("Operation '" + opname + "' doesn't exist"); 00169 } 00170 else 00171 { 00172 // Create an operationplan 00173 opplan = oper->createOperationPlan(0.0,Date::infinitePast,Date::infinitePast,NULL,NULL,id,false); 00174 if (!opplan->getType().raiseEvent(opplan, SIG_ADD)) 00175 { 00176 delete opplan; 00177 throw DataException("Can't create operationplan"); 00178 } 00179 return opplan; 00180 } 00181 } 00182 00183 00184 DECLARE_EXPORT OperationPlan* OperationPlan::findId(unsigned long l) 00185 { 00186 // We are garantueed that there are no operationplans that have an id equal 00187 // or higher than the current counter. This is garantueed by the 00188 // instantiate() method. 00189 if (l >= counterMin && l <= counterMax) return NULL; 00190 00191 // Loop through all operationplans. 00192 for (OperationPlan::iterator i = begin(); i != end(); ++i) 00193 if (i->id == l) return &*i; 00194 00195 // This ID was not found 00196 return NULL; 00197 } 00198 00199 00200 DECLARE_EXPORT bool OperationPlan::instantiate(bool useMinCounter) 00201 { 00202 // At least a valid operation pointer must exist 00203 if (!oper) throw LogicException("Initializing an invalid operationplan"); 00204 00205 // Avoid zero quantity on top-operationplans 00206 if (getQuantity() <= 0.0 && !owner) 00207 { 00208 delete this; 00209 return false; 00210 } 00211 00212 // Call any operation specific initialisation logic 00213 if (!oper->extraInstantiate(this)) 00214 { 00215 delete this; 00216 return false; 00217 } 00218 00219 // Instantiate all suboperationplans as well 00220 for (OperationPlan::iterator x(this); x != end(); ++x) 00221 x->instantiate(); 00222 00223 // Create unique identifier 00224 // Having an identifier assigned is an important flag. 00225 // Only operation plans with an id : 00226 // - can be linked in the global operation plan list. 00227 // - can have problems (this results from the previous point). 00228 // - can be linked with a demand. 00229 // These properties allow us to delete operation plans without an id faster. 00230 static Mutex onlyOne; 00231 { 00232 ScopeMutexLock l(onlyOne); // Need to assure that ids are unique! 00233 if (id) 00234 { 00235 // An identifier was read in from input 00236 if (id < counterMin || id > counterMax) 00237 { 00238 // The assigned id potentially clashes with an existing operationplan. 00239 // Check whether it clashes with existing operationplans 00240 OperationPlan* opplan = findId(id); 00241 if (opplan && opplan->getOperation()!=oper) 00242 { 00243 ostringstream ch; 00244 ch << "Operationplan id " << id 00245 << " defined multiple times with different operations: '" 00246 << opplan->getOperation() << "' & '" << oper << "'"; 00247 delete this; 00248 throw DataException(ch.str()); 00249 } 00250 } 00251 // The new operationplan definately doesn't clash with existing id's. 00252 // The counter need updating to garantuee that counter is always 00253 // a safe starting point for tagging new operationplans. 00254 else if (useMinCounter) 00255 counterMin = id+1; 00256 else 00257 counterMax = id-1; 00258 } 00259 // Fresh operationplan with blank id 00260 else if (useMinCounter) 00261 id = counterMin++; 00262 else 00263 id = counterMax--; 00264 // Check whether the counters are still okay 00265 if (counterMin >= counterMax) 00266 throw RuntimeException("Exhausted the range of available operationplan identifiers"); 00267 } 00268 00269 // Insert into the doubly linked list of operationplans. 00270 insertInOperationplanList(); 00271 00272 // If we used the lazy creator, the flow- and loadplans have not been 00273 // created yet. We do it now... 00274 createFlowLoads(); 00275 00276 // Extra registration step if this is a delivery operation 00277 if (getDemand() && getDemand()->getDeliveryOperation() == oper) 00278 dmd->addDelivery(this); 00279 00280 // Mark the operation to detect its problems 00281 // Note that a single operationplan thus retriggers the problem computation 00282 // for all operationplans of this operation. For models with 1) a large 00283 // number of operationplans per operation and 2) very frequent problem 00284 // detection, this could constitute a scalability problem. This combination 00285 // is expected to be unusual and rare, justifying this design choice. 00286 oper->setChanged(); 00287 00288 // The operationplan is valid 00289 return true; 00290 } 00291 00292 00293 DECLARE_EXPORT void OperationPlan::insertInOperationplanList() 00294 { 00295 00296 // Check if already linked 00297 if (prev || oper->first_opplan == this) return; 00298 00299 if (!oper->first_opplan) 00300 { 00301 // First operationplan in the list 00302 oper->first_opplan = this; 00303 oper->last_opplan = this; 00304 } 00305 else if (*this < *(oper->first_opplan)) 00306 { 00307 // First in the list 00308 next = oper->first_opplan; 00309 next->prev = this; 00310 oper->first_opplan = this; 00311 } 00312 else if (*(oper->last_opplan) < *this) 00313 { 00314 // Last in the list 00315 prev = oper->last_opplan; 00316 prev->next = this; 00317 oper->last_opplan = this; 00318 } 00319 else 00320 { 00321 // Insert in the middle of the list 00322 OperationPlan *x = oper->last_opplan; 00323 OperationPlan *y = NULL; 00324 while (!(*x < *this)) 00325 { 00326 y = x; 00327 x = x->prev; 00328 } 00329 next = y; 00330 prev = x; 00331 if (x) x->next = this; 00332 if (y) y->prev = this; 00333 } 00334 } 00335 00336 00337 DECLARE_EXPORT void OperationPlan::addSubOperationPlan(OperationPlan* o) 00338 { 00339 // Check 00340 if (!o) throw LogicException("Adding null suboperationplan"); 00341 00342 // Adding a suboperationplan that was already added 00343 if (o->owner == this) return; 00344 00345 // Clear the previous owner, if there is one 00346 if (o->owner) o->owner->eraseSubOperationPlan(o); 00347 00348 // Link in the list, keeping the right ordering 00349 if (!firstsubopplan) 00350 { 00351 // First element 00352 firstsubopplan = o; 00353 lastsubopplan = o; 00354 } 00355 else if (firstsubopplan->getOperation() != OperationSetup::setupoperation) 00356 { 00357 // New head 00358 o->nextsubopplan = firstsubopplan; 00359 firstsubopplan->prevsubopplan = o; 00360 firstsubopplan = o; 00361 } 00362 else 00363 { 00364 // Insert right after the setup operationplan 00365 OperationPlan *s = firstsubopplan->nextsubopplan; 00366 o->nextsubopplan = s; 00367 if (s) s->nextsubopplan = o; 00368 else lastsubopplan = o; 00369 } 00370 00371 o->owner = this; 00372 00373 // Update the flow and loadplans 00374 update(); 00375 } 00376 00377 00378 DECLARE_EXPORT void OperationPlan::eraseSubOperationPlan(OperationPlan* o) 00379 { 00380 // Check 00381 if (!o) return; 00382 00383 // Adding a suboperationplan that was already added 00384 if (o->owner != this) 00385 throw LogicException("Operationplan isn't a suboperationplan"); 00386 00387 // Clear owner field 00388 o->owner = NULL; 00389 00390 // Remove from the list 00391 if (o->prevsubopplan) 00392 o->prevsubopplan->nextsubopplan = o->nextsubopplan; 00393 else 00394 firstsubopplan = o->nextsubopplan; 00395 if (o->nextsubopplan) 00396 o->nextsubopplan->prevsubopplan = o->prevsubopplan; 00397 else 00398 lastsubopplan = o->prevsubopplan; 00399 }; 00400 00401 00402 DECLARE_EXPORT bool OperationPlan::operator < (const OperationPlan& a) const 00403 { 00404 // Different operations 00405 if (oper != a.oper) 00406 return *oper < *(a.oper); 00407 00408 // Different start date 00409 if (dates.getStart() != a.dates.getStart()) 00410 return dates.getStart() < a.dates.getStart(); 00411 00412 // Sort based on quantity 00413 return quantity >= a.quantity; 00414 } 00415 00416 00417 DECLARE_EXPORT void OperationPlan::createFlowLoads() 00418 { 00419 // Has been initialized already, it seems 00420 if (firstflowplan || firstloadplan) return; 00421 00422 // Create setup suboperationplans and loadplans 00423 for (Operation::loadlist::const_iterator g=oper->getLoads().begin(); 00424 g!=oper->getLoads().end(); ++g) 00425 if (!g->getAlternate()) 00426 { 00427 new LoadPlan(this, &*g); 00428 if (!g->getSetup().empty() && g->getResource()->getSetupMatrix()) 00429 OperationSetup::setupoperation->createOperationPlan( 00430 1, getDates().getStart(), getDates().getStart(), NULL, this); 00431 } 00432 00433 // Create flowplans for flows that are not alternates of another one 00434 for (Operation::flowlist::const_iterator h=oper->getFlows().begin(); 00435 h!=oper->getFlows().end(); ++h) 00436 if (!h->getAlternate()) new FlowPlan(this, &*h); 00437 } 00438 00439 00440 DECLARE_EXPORT OperationPlan::~OperationPlan() 00441 { 00442 // Initialize 00443 FlowPlanIterator e = beginFlowPlans(); 00444 LoadPlanIterator f = beginLoadPlans(); 00445 OperationPlan *x = firstsubopplan; 00446 00447 // Reset to avoid extra updates during the destruction 00448 firstflowplan = NULL; 00449 firstloadplan = NULL; 00450 firstsubopplan = NULL; 00451 lastsubopplan = NULL; 00452 00453 // Delete the flowplans and loadplan 00454 while (e != endFlowPlans()) delete &*(e++); 00455 while (f != endLoadPlans()) delete &*(f++); 00456 00457 // Delete the sub operationplans 00458 while (x) 00459 { 00460 OperationPlan *y = x->nextsubopplan; 00461 x->owner = NULL; // Need to clear before destroying the suboperationplan 00462 delete x; 00463 x = y; 00464 } 00465 00466 // Delete also the owner 00467 if (owner) 00468 { 00469 const OperationPlan* o = owner; 00470 setOwner(NULL); 00471 delete o; 00472 } 00473 00474 // Delete from the list of deliveries 00475 if (id && dmd) dmd->removeDelivery(this); 00476 00477 // Delete from the operationplan list 00478 if (prev) 00479 // In the middle 00480 prev->next = next; 00481 else if (oper->first_opplan == this) 00482 // First opplan in the list of this operation 00483 oper->first_opplan = next; 00484 if (next) 00485 // In the middle 00486 next->prev = prev; 00487 else if (oper->last_opplan == this) 00488 // Last opplan in the list of this operation 00489 oper->last_opplan = prev; 00490 } 00491 00492 00493 void DECLARE_EXPORT OperationPlan::setOwner(OperationPlan* o) 00494 { 00495 // Special case: the same owner is set twice 00496 if (owner == o) return; 00497 // Erase the previous owner if there is one 00498 if (owner) owner->eraseSubOperationPlan(this); 00499 // Register with the new owner 00500 if (o) o->addSubOperationPlan(this); 00501 } 00502 00503 00504 void DECLARE_EXPORT OperationPlan::setStart (Date d) 00505 { 00506 // Locked opplans don't move 00507 if (getLocked()) return; 00508 00509 if (!lastsubopplan || lastsubopplan->getOperation() == OperationSetup::setupoperation) 00510 // No sub operationplans 00511 oper->setOperationPlanParameters(this,quantity,d,Date::infinitePast); 00512 else 00513 { 00514 // Move all sub-operationplans in an orderly fashion 00515 bool firstMove = true; 00516 for (OperationPlan* i = firstsubopplan; i; i = i->nextsubopplan) 00517 { 00518 if (i->getOperation() == OperationSetup::setupoperation) continue; 00519 if (i->getDates().getStart() < d || firstMove) 00520 { 00521 i->setStart(d); 00522 // Comment out the next line to leave slack in the routing. 00523 // With the line commented out routing slack is cleaned up aggressively. 00524 //@todo firstMove = false; 00525 d = i->getDates().getEnd(); 00526 } 00527 else 00528 // There is sufficient slack between the suboperation plans 00529 break; 00530 } 00531 } 00532 00533 // Update flow and loadplans 00534 update(); 00535 } 00536 00537 00538 void DECLARE_EXPORT OperationPlan::setEnd(Date d) 00539 { 00540 // Locked opplans don't move 00541 if (getLocked()) return; 00542 00543 if (!lastsubopplan || lastsubopplan->getOperation() == OperationSetup::setupoperation) 00544 // No sub operationplans 00545 oper->setOperationPlanParameters(this,quantity,Date::infinitePast,d); 00546 else 00547 { 00548 // Move all sub-operationplans in an orderly fashion 00549 bool firstMove = true; 00550 for (OperationPlan* i = lastsubopplan; i; i = i->prevsubopplan) 00551 { 00552 if (i->getOperation() == OperationSetup::setupoperation) break; 00553 if (i->getDates().getEnd() > d || firstMove) 00554 { 00555 i->setEnd(d); 00556 // Comment out the next line to leave slack in the routing. 00557 // With the line commented out routing slack is cleaned up aggressively. 00558 // @todo firstMove = false; 00559 d = i->getDates().getStart(); 00560 } 00561 else 00562 // There is sufficient slack between the suboperations 00563 break; 00564 } 00565 } 00566 00567 // Update flow and loadplans 00568 update(); 00569 } 00570 00571 00572 DECLARE_EXPORT double OperationPlan::setQuantity (double f, bool roundDown, bool upd, bool execute) 00573 { 00574 // No impact on locked operationplans 00575 if (getLocked()) return quantity; 00576 00577 // Invalid operationplan: the quantity must be >= 0. 00578 if (f < 0) 00579 throw DataException("Operationplans can't have negative quantities"); 00580 00581 // Setting a quantity is only allowed on a top operationplan. 00582 // One exception: on alternate operations the sizing on the sub-operations is 00583 // respected. 00584 if (owner && owner->getOperation()->getType() != *OperationAlternate::metadata) 00585 return owner->setQuantity(f,roundDown,upd,execute); 00586 00587 // Compute the correct size for the operationplan 00588 if (f!=0.0 && getOperation()->getSizeMinimum()>0.0 00589 && f < getOperation()->getSizeMinimum()) 00590 { 00591 if (roundDown) 00592 { 00593 // Smaller than the minimum quantity, rounding down means... nothing 00594 if (!execute) return 0.0; 00595 quantity = 0.0; 00596 // Update the flow and loadplans, and mark for problem detection 00597 if (upd) update(); 00598 return 0.0; 00599 } 00600 f = getOperation()->getSizeMinimum(); 00601 } 00602 if (f != 0.0 && f >= getOperation()->getSizeMaximum()) 00603 { 00604 roundDown = true; // force rounddown to stay below the limit 00605 f = getOperation()->getSizeMaximum(); 00606 } 00607 if (f!=0.0 && getOperation()->getSizeMultiple()>0.0) 00608 { 00609 int mult = static_cast<int> (f / getOperation()->getSizeMultiple() 00610 + (roundDown ? 0.0 : 0.99999999)); 00611 if (!execute) return mult * getOperation()->getSizeMultiple(); 00612 quantity = mult * getOperation()->getSizeMultiple(); 00613 } 00614 else 00615 { 00616 if (!execute) return f; 00617 quantity = f; 00618 } 00619 00620 // Update the parent of an alternate operationplan 00621 if (execute && owner 00622 && owner->getOperation()->getType() == *OperationAlternate::metadata) 00623 { 00624 owner->quantity = quantity; 00625 if (upd) owner->resizeFlowLoadPlans(); 00626 } 00627 00628 // Apply the same size also to its children 00629 if (execute && firstsubopplan) 00630 for (OperationPlan *i = firstsubopplan; i; i = i->nextsubopplan) 00631 if (i->getOperation() != OperationSetup::setupoperation) 00632 { 00633 i->quantity = quantity; 00634 if (upd) i->resizeFlowLoadPlans(); 00635 } 00636 00637 // Update the flow and loadplans, and mark for problem detection 00638 if (upd) update(); 00639 return quantity; 00640 } 00641 00642 00643 DECLARE_EXPORT void OperationPlan::resizeFlowLoadPlans() 00644 { 00645 // Update all flowplans 00646 for (FlowPlanIterator ee = beginFlowPlans(); ee != endFlowPlans(); ++ee) 00647 ee->update(); 00648 00649 // Update all loadplans 00650 for (LoadPlanIterator e = beginLoadPlans(); e != endLoadPlans(); ++e) 00651 e->update(); 00652 00653 // Align the end of the setup operationplan with the start of the operation 00654 if (firstsubopplan && firstsubopplan->getOperation() == OperationSetup::setupoperation 00655 && firstsubopplan->getDates().getEnd() != getDates().getStart()) 00656 firstsubopplan->setEnd(getDates().getStart()); 00657 else if (getOperation() == OperationSetup::setupoperation 00658 && getDates().getEnd() != getOwner()->getDates().getStart()) 00659 getOwner()->setStart(getDates().getEnd()); 00660 00661 // Allow the operation length to be changed now that the quantity has changed 00662 // Note that we assume that the end date remains fixed. This assumption makes 00663 // sense if the operationplan was created to satisfy a demand. 00664 // It is not valid though when the purpose of the operationplan was to push 00665 // some material downstream. 00666 00667 // Resize children 00668 for (OperationPlan *j = firstsubopplan; j; j = j->nextsubopplan) 00669 if (j->getOperation() != OperationSetup::setupoperation) 00670 { 00671 j->quantity = quantity; 00672 j->resizeFlowLoadPlans(); 00673 } 00674 00675 // Notify the demand of the changed delivery 00676 if (dmd) dmd->setChanged(); 00677 } 00678 00679 00680 DECLARE_EXPORT void OperationPlan::update() 00681 { 00682 if (lastsubopplan && lastsubopplan->getOperation() != OperationSetup::setupoperation) 00683 { 00684 // Inherit the start and end date of the child operationplans 00685 OperationPlan *tmp = firstsubopplan; 00686 if (tmp->getOperation() == OperationSetup::setupoperation) 00687 tmp = tmp->nextsubopplan; 00688 dates.setStartAndEnd( 00689 tmp->getDates().getStart(), 00690 lastsubopplan->getDates().getEnd() 00691 ); 00692 // If at least 1 sub-operationplan is locked, the parent must be locked 00693 flags &= ~IS_LOCKED; // Clear is_locked flag 00694 for (OperationPlan* i = firstsubopplan; i; i = i->nextsubopplan) 00695 if (i->flags & IS_LOCKED) 00696 { 00697 flags |= IS_LOCKED; // Set is_locked flag 00698 break; 00699 } 00700 } 00701 00702 // Update the flow and loadplans 00703 resizeFlowLoadPlans(); 00704 00705 // Notify the owner operationplan 00706 if (owner) owner->update(); 00707 00708 // Mark as changed 00709 setChanged(); 00710 } 00711 00712 00713 DECLARE_EXPORT void OperationPlan::deleteOperationPlans(Operation* o, bool deleteLockedOpplans) 00714 { 00715 if (!o) return; 00716 for (OperationPlan *opplan = o->first_opplan; opplan; ) 00717 { 00718 OperationPlan *tmp = opplan; 00719 opplan = opplan->next; 00720 // Note that the deletion of the operationplan also updates the opplan list 00721 if (deleteLockedOpplans || !tmp->getLocked()) delete tmp; 00722 } 00723 } 00724 00725 00726 DECLARE_EXPORT double OperationPlan::getPenalty() const 00727 { 00728 double penalty = 0; 00729 for (OperationPlan::LoadPlanIterator i = beginLoadPlans(); 00730 i != endLoadPlans(); ++i) 00731 if (i->isStart() && !i->getLoad()->getSetup().empty() && i->getResource()->getSetupMatrix()) 00732 { 00733 SetupMatrix::Rule *rule = i->getResource()->getSetupMatrix() 00734 ->calculateSetup(i->getSetup(false), i->getSetup(true)); 00735 if (rule) penalty += rule->getCost(); 00736 } 00737 return penalty; 00738 } 00739 00740 00741 DECLARE_EXPORT bool OperationPlan::isExcess(bool strict) const 00742 { 00743 // Delivery operationplans aren't excess 00744 if (getDemand()) return false; 00745 00746 // Recursive call for suboperationplans 00747 for (OperationPlan* subopplan = firstsubopplan; subopplan; subopplan = subopplan->nextsubopplan) 00748 if (!subopplan->isExcess()) return false; 00749 00750 // Loop over all producing flowplans 00751 for (OperationPlan::FlowPlanIterator i = beginFlowPlans(); 00752 i != endFlowPlans(); ++i) 00753 { 00754 // Skip consuming flowplans 00755 if (i->getQuantity() <= 0) continue; 00756 00757 // Loop over all flowplans in the buffer (starting at the end) and verify 00758 // that the onhand is bigger than the flowplan quantity 00759 double current_maximum(0.0); 00760 double current_minimum(0.0); 00761 Buffer::flowplanlist::const_iterator j = i->getBuffer()->getFlowPlans().rbegin(); 00762 if (!strict && j != i->getBuffer()->getFlowPlans().end()) 00763 { 00764 current_maximum = i->getBuffer()->getFlowPlans().getMax(&*j); 00765 current_minimum = i->getBuffer()->getFlowPlans().getMin(&*j); 00766 } 00767 for (; j != i->getBuffer()->getFlowPlans().end(); --j) 00768 { 00769 if ( (current_maximum > 0 00770 && j->getOnhand() < i->getQuantity() + current_maximum - ROUNDING_ERROR) 00771 || j->getOnhand() < i->getQuantity() + current_minimum - ROUNDING_ERROR ) 00772 return false; 00773 if (j->getType() == 4 && !strict) current_maximum = j->getMax(false); 00774 if (j->getType() == 3 && !strict) current_minimum = j->getMin(false); 00775 if (&*j == &*i) break; 00776 } 00777 } 00778 00779 // If we remove this operationplan the onhand in all buffers remains positive. 00780 return true; 00781 } 00782 00783 00784 DECLARE_EXPORT TimePeriod OperationPlan::getUnavailable() const 00785 { 00786 TimePeriod x; 00787 DateRange y = getOperation()->calculateOperationTime(dates.getStart(), dates.getEnd(), &x); 00788 return dates.getDuration() - x; 00789 } 00790 00791 00792 DECLARE_EXPORT void OperationPlan::writer(const MetaCategory* c, XMLOutput* o) 00793 { 00794 if (!empty()) 00795 { 00796 o->BeginObject(*c->grouptag); 00797 for (iterator i=begin(); i!=end(); ++i) 00798 o->writeElement(*c->typetag, *i); 00799 o->EndObject(*c->grouptag); 00800 } 00801 } 00802 00803 00804 DECLARE_EXPORT void OperationPlan::writeElement(XMLOutput *o, const Keyword& tag, mode m) const 00805 { 00806 // Don't export operationplans of hidden operations 00807 if (oper->getHidden()) return; 00808 00809 // Writing a reference 00810 if (m == REFERENCE) 00811 { 00812 o->writeElement 00813 (tag, Tags::tag_id, id, Tags::tag_operation, oper->getName()); 00814 return; 00815 } 00816 00817 if (m != NOHEADER) 00818 o->BeginObject(tag, Tags::tag_id, id, Tags::tag_operation,oper->getName()); 00819 00820 // The demand reference is only valid for delivery operationplans, 00821 // and it should only be written if this tag is not being written 00822 // as part of a demand+delivery tag. 00823 if (dmd && !dynamic_cast<Demand*>(o->getPreviousObject())) 00824 o->writeElement(Tags::tag_demand, dmd); 00825 00826 o->writeElement(Tags::tag_start, dates.getStart()); 00827 o->writeElement(Tags::tag_end, dates.getEnd()); 00828 o->writeElement(Tags::tag_quantity, quantity); 00829 if (getLocked()) o->writeElement (Tags::tag_locked, getLocked()); 00830 o->writeElement(Tags::tag_owner, owner); 00831 00832 // Write out the flowplans and their pegging 00833 if (o->getContentType() == XMLOutput::PLANDETAIL) 00834 { 00835 o->BeginObject(Tags::tag_flowplans); 00836 for (FlowPlanIterator qq = beginFlowPlans(); qq != endFlowPlans(); ++qq) 00837 qq->writeElement(o, Tags::tag_flowplan); 00838 o->EndObject(Tags::tag_flowplans); 00839 } 00840 00841 o->EndObject(tag); 00842 } 00843 00844 00845 DECLARE_EXPORT void OperationPlan::beginElement(XMLInput& pIn, const Attribute& pAttr) 00846 { 00847 if (pAttr.isA (Tags::tag_demand)) 00848 pIn.readto( Demand::reader(Demand::metadata,pIn.getAttributes()) ); 00849 else if (pAttr.isA(Tags::tag_owner)) 00850 pIn.readto(createOperationPlan(metadata,pIn.getAttributes())); 00851 else if (pAttr.isA(Tags::tag_flowplans)) 00852 pIn.IgnoreElement(); 00853 } 00854 00855 00856 DECLARE_EXPORT void OperationPlan::endElement (XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement) 00857 { 00858 // Note that the fields have been ordered more or less in the order 00859 // of their expected frequency. 00860 // Note that id and operation are handled already during the 00861 // operationplan creation. They don't need to be handled here... 00862 if (pAttr.isA(Tags::tag_quantity)) 00863 pElement >> quantity; 00864 else if (pAttr.isA(Tags::tag_start)) 00865 dates.setStart(pElement.getDate()); 00866 else if (pAttr.isA(Tags::tag_end)) 00867 dates.setEnd(pElement.getDate()); 00868 else if (pAttr.isA(Tags::tag_owner) && !pIn.isObjectEnd()) 00869 { 00870 OperationPlan* o = dynamic_cast<OperationPlan*>(pIn.getPreviousObject()); 00871 if (o) setOwner(o); 00872 } 00873 else if (pIn.isObjectEnd()) 00874 { 00875 // Initialize the operationplan 00876 if (!instantiate()) 00877 // Initialization failed and the operationplan is deleted 00878 pIn.invalidateCurrentObject(); 00879 } 00880 else if (pAttr.isA (Tags::tag_demand)) 00881 { 00882 Demand * d = dynamic_cast<Demand*>(pIn.getPreviousObject()); 00883 if (d) d->addDelivery(this); 00884 else throw LogicException("Incorrect object type during read operation"); 00885 } 00886 else if (pAttr.isA(Tags::tag_locked)) 00887 setLocked(pElement.getBool()); 00888 } 00889 00890 00891 DECLARE_EXPORT void OperationPlan::setLocked(bool b) 00892 { 00893 if (b) 00894 flags |= IS_LOCKED; 00895 else 00896 flags &= ~IS_LOCKED; 00897 for (OperationPlan *x = firstsubopplan; x; x = x->nextsubopplan) 00898 x->setLocked(b); 00899 update(); 00900 } 00901 00902 00903 DECLARE_EXPORT void OperationPlan::setDemand(Demand* l) 00904 { 00905 // No change 00906 if (l==dmd) return; 00907 00908 // Unregister from previous lot 00909 if (dmd) dmd->removeDelivery(this); 00910 00911 // Register the new demand and mark it changed 00912 dmd = l; 00913 if (l) l->setChanged(); 00914 } 00915 00916 00917 PyObject* OperationPlan::create(PyTypeObject* pytype, PyObject* args, PyObject* kwds) 00918 { 00919 try 00920 { 00921 // Find or create the C++ object 00922 PythonAttributeList atts(kwds); 00923 Object* x = createOperationPlan(OperationPlan::metadata,atts); 00924 00925 // Iterate over extra keywords, and set attributes. @todo move this responsability to the readers... 00926 if (x) 00927 { 00928 PyObject *key, *value; 00929 Py_ssize_t pos = 0; 00930 while (PyDict_Next(kwds, &pos, &key, &value)) 00931 { 00932 PythonObject field(value); 00933 Attribute attr(PyString_AsString(key)); 00934 if (!attr.isA(Tags::tag_operation) && !attr.isA(Tags::tag_id) && !attr.isA(Tags::tag_action)) 00935 { 00936 int result = x->setattro(attr, field); 00937 if (result && !PyErr_Occurred()) 00938 PyErr_Format(PyExc_AttributeError, 00939 "attribute '%s' on '%s' can't be updated", 00940 PyString_AsString(key), x->ob_type->tp_name); 00941 } 00942 }; 00943 } 00944 00945 if (x && !static_cast<OperationPlan*>(x)->instantiate()) 00946 return NULL; 00947 return x; 00948 } 00949 catch (...) 00950 { 00951 PythonType::evalException(); 00952 return NULL; 00953 } 00954 } 00955 00956 00957 DECLARE_EXPORT PyObject* OperationPlan::getattro(const Attribute& attr) 00958 { 00959 if (attr.isA(Tags::tag_id)) 00960 return PythonObject(getIdentifier()); 00961 if (attr.isA(Tags::tag_operation)) 00962 return PythonObject(getOperation()); 00963 if (attr.isA(Tags::tag_flowplans)) 00964 return new frepple::FlowPlanIterator(this); 00965 if (attr.isA(Tags::tag_loadplans)) 00966 return new frepple::LoadPlanIterator(this); 00967 if (attr.isA(Tags::tag_quantity)) 00968 return PythonObject(getQuantity()); 00969 if (attr.isA(Tags::tag_start)) 00970 return PythonObject(getDates().getStart()); 00971 if (attr.isA(Tags::tag_end)) 00972 return PythonObject(getDates().getEnd()); 00973 if (attr.isA(Tags::tag_demand)) 00974 return PythonObject(getDemand()); 00975 if (attr.isA(Tags::tag_locked)) 00976 return PythonObject(getLocked()); 00977 if (attr.isA(Tags::tag_owner)) 00978 return PythonObject(getOwner()); 00979 if (attr.isA(Tags::tag_operationplans)) 00980 return new OperationPlanIterator(this); 00981 if (attr.isA(Tags::tag_hidden)) 00982 return PythonObject(getHidden()); 00983 if (attr.isA(Tags::tag_unavailable)) 00984 return PythonObject(getUnavailable()); 00985 return NULL; 00986 } 00987 00988 00989 DECLARE_EXPORT int OperationPlan::setattro(const Attribute& attr, const PythonObject& field) 00990 { 00991 if (attr.isA(Tags::tag_quantity)) 00992 setQuantity(field.getDouble()); 00993 else if (attr.isA(Tags::tag_start)) 00994 setStart(field.getDate()); 00995 else if (attr.isA(Tags::tag_end)) 00996 setEnd(field.getDate()); 00997 else if (attr.isA(Tags::tag_locked)) 00998 setLocked(field.getBool()); 00999 else if (attr.isA(Tags::tag_demand)) 01000 { 01001 if (!field.check(Demand::metadata)) 01002 { 01003 PyErr_SetString(PythonDataException, "operationplan demand must be of type demand"); 01004 return -1; 01005 } 01006 Demand* y = static_cast<Demand*>(static_cast<PyObject*>(field)); 01007 setDemand(y); 01008 } 01009 else if (attr.isA(Tags::tag_owner)) 01010 { 01011 if (!field.check(OperationPlan::metadata)) 01012 { 01013 PyErr_SetString(PythonDataException, "operationplan demand must be of type demand"); 01014 return -1; 01015 } 01016 OperationPlan* y = static_cast<OperationPlan*>(static_cast<PyObject*>(field)); 01017 setOwner(y); 01018 } 01019 else 01020 return -1; 01021 return 0; 01022 } 01023 01024 } // end namespace
Documentation generated for frePPLe by
