operation.cpp
Go to the documentation of this file.
00001 /*************************************************************************** 00002 file : $URL: http://svn.code.sf.net/p/frepple/code/trunk/src/model/operation.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 template<class Operation> DECLARE_EXPORT Tree utils::HasName<Operation>::st; 00034 DECLARE_EXPORT const MetaCategory* Operation::metadata; 00035 DECLARE_EXPORT const MetaClass* OperationFixedTime::metadata, 00036 *OperationTimePer::metadata, 00037 *OperationRouting::metadata, 00038 *OperationAlternate::metadata, 00039 *OperationSetup::metadata; 00040 DECLARE_EXPORT Operation::Operationlist Operation::nosubOperations; 00041 DECLARE_EXPORT const Operation* OperationSetup::setupoperation; 00042 00043 00044 int Operation::initialize() 00045 { 00046 // Initialize the metadata 00047 metadata = new MetaCategory("operation", "operations", reader, writer); 00048 00049 // Initialize the Python class 00050 return FreppleCategory<Operation>::initialize(); 00051 } 00052 00053 00054 int OperationFixedTime::initialize() 00055 { 00056 // Initialize the metadata 00057 metadata = new MetaClass("operation", "operation_fixed_time", 00058 Object::createString<OperationFixedTime>, true); 00059 00060 // Initialize the Python class 00061 return FreppleClass<OperationFixedTime,Operation>::initialize(); 00062 } 00063 00064 00065 int OperationTimePer::initialize() 00066 { 00067 // Initialize the metadata 00068 metadata = new MetaClass("operation", "operation_time_per", 00069 Object::createString<OperationTimePer>); 00070 00071 // Initialize the Python class 00072 return FreppleClass<OperationTimePer,Operation>::initialize(); 00073 } 00074 00075 00076 int OperationAlternate::initialize() 00077 { 00078 // Initialize the metadata 00079 metadata = new MetaClass("operation", "operation_alternate", 00080 Object::createString<OperationAlternate>); 00081 00082 // Initialize the Python class 00083 FreppleClass<OperationAlternate,Operation>::getType().addMethod("addAlternate", OperationAlternate::addAlternate, METH_KEYWORDS, "add an alternate"); 00084 return FreppleClass<OperationAlternate,Operation>::initialize(); 00085 } 00086 00087 00088 int OperationRouting::initialize() 00089 { 00090 // Initialize the metadata 00091 metadata = new MetaClass("operation", "operation_routing", 00092 Object::createString<OperationRouting>); 00093 00094 // Initialize the Python class 00095 FreppleClass<OperationRouting,Operation>::getType().addMethod("addStep", OperationRouting::addStep, METH_VARARGS , "add steps to the routing"); 00096 return FreppleClass<OperationRouting,Operation>::initialize(); 00097 } 00098 00099 00100 int OperationSetup::initialize() 00101 { 00102 // Initialize the metadata. 00103 // There is NO factory method 00104 metadata = new MetaClass("operation", "operation_setup"); 00105 00106 // Initialize the Python class 00107 int tmp = FreppleClass<OperationSetup,Operation>::initialize(); 00108 00109 // Create a generic setup operation. 00110 // This will be the only instance of this class. 00111 setupoperation = add(new OperationSetup("setup operation")); 00112 00113 return tmp; 00114 } 00115 00116 00117 DECLARE_EXPORT Operation::~Operation() 00118 { 00119 // Delete all existing operationplans (even locked ones) 00120 deleteOperationPlans(true); 00121 00122 // The Flow and Load objects are automatically deleted by the destructor 00123 // of the Association list class. 00124 00125 // Remove the reference to this operation from all items 00126 for (Item::iterator k = Item::begin(); k != Item::end(); ++k) 00127 if (k->getOperation() == this) k->setOperation(NULL); 00128 00129 // Remove the reference to this operation from all demands 00130 for (Demand::iterator l = Demand::begin(); l != Demand::end(); ++l) 00131 if (l->getOperation() == this) l->setOperation(NULL); 00132 00133 // Remove the reference to this operation from all buffers 00134 for (Buffer::iterator m = Buffer::begin(); m != Buffer::end(); ++m) 00135 if (m->getProducingOperation() == this) m->setProducingOperation(NULL); 00136 00137 // Remove the operation from its super-operations and sub-operations 00138 // Note that we are not using a for-loop since our function is actually 00139 // updating the list of super-operations at the same time as we move 00140 // through it. 00141 while (!getSuperOperations().empty()) 00142 removeSuperOperation(*getSuperOperations().begin()); 00143 } 00144 00145 00146 DECLARE_EXPORT OperationRouting::~OperationRouting() 00147 { 00148 // Note that we are not using a for-loop since our function is actually 00149 // updating the list of super-operations at the same time as we move 00150 // through it. 00151 while (!getSubOperations().empty()) 00152 removeSubOperation(*getSubOperations().begin()); 00153 } 00154 00155 00156 DECLARE_EXPORT OperationAlternate::~OperationAlternate() 00157 { 00158 // Note that we are not using a for-loop since our function is actually 00159 // updating the list of super-operations at the same time as we move 00160 // through it. 00161 while (!getSubOperations().empty()) 00162 removeSubOperation(*getSubOperations().begin()); 00163 } 00164 00165 00166 DECLARE_EXPORT OperationPlan* Operation::createOperationPlan (double q, Date s, Date e, 00167 Demand* l, OperationPlan* ow, unsigned long i, 00168 bool makeflowsloads) const 00169 { 00170 OperationPlan *opplan = new OperationPlan(); 00171 initOperationPlan(opplan,q,s,e,l,ow,i,makeflowsloads); 00172 return opplan; 00173 } 00174 00175 00176 DECLARE_EXPORT DateRange Operation::calculateOperationTime 00177 (Date thedate, TimePeriod duration, bool forward, 00178 TimePeriod *actualduration) const 00179 { 00180 int calcount = 0; 00181 // Initial size of 10 should do for 99.99% of all cases 00182 vector<Calendar::EventIterator*> cals(10); 00183 00184 // Default actual duration 00185 if (actualduration) *actualduration = duration; 00186 00187 try 00188 { 00189 // Step 1: Create an iterator on each of the calendars 00190 // a) operation's location 00191 if (loc && loc->getAvailable()) 00192 cals[calcount++] = new Calendar::EventIterator(loc->getAvailable(), thedate, forward); 00193 /* @todo multiple availability calendars are not implemented yet 00194 for (Operation::loadlist::const_iterator g=loaddata.begin(); 00195 g!=loaddata.end(); ++g) 00196 { 00197 Resource* res = g->getResource(); 00198 if (res->getMaximum()) 00199 // b) resource size calendar 00200 cals[calcount++] = new Calendar::EventIterator( 00201 res->getMaximum(), 00202 thedate 00203 ); 00204 if (res->getLocation() && res->getLocation()->getAvailable()) 00205 // c) resource location 00206 cals[calcount++] = new Calendar::EventIterator( 00207 res->getLocation()->getAvailable(), 00208 thedate 00209 ); 00210 } 00211 */ 00212 00213 // Special case: no calendars at all 00214 if (calcount == 0) 00215 return forward ? 00216 DateRange(thedate, thedate+duration) : 00217 DateRange(thedate-duration, thedate); 00218 00219 // Step 2: Iterate over the calendar dates to find periods where all 00220 // calendars are simultaneously effective. 00221 DateRange result; 00222 Date curdate = thedate; 00223 bool status = false; 00224 TimePeriod curduration = duration; 00225 while (true) 00226 { 00227 // Check whether all calendars are available 00228 bool available = true; 00229 for (int c = 0; c < calcount && available; c++) 00230 { 00231 const Calendar::Bucket *tmp = cals[c]->getBucket(); 00232 if (tmp) 00233 available = tmp->getBool(); 00234 else 00235 available = cals[c]->getCalendar()->getBool(); 00236 } 00237 curdate = cals[0]->getDate(); 00238 00239 if (available && !status) 00240 { 00241 // Becoming available after unavailable period 00242 thedate = curdate; 00243 status = true; 00244 if (forward && result.getStart() == Date::infinitePast) 00245 // First available time - make operation start at this time 00246 result.setStart(curdate); 00247 else if (!forward && result.getEnd() == Date::infiniteFuture) 00248 // First available time - make operation end at this time 00249 result.setEnd(curdate); 00250 } 00251 else if (!available && status) 00252 { 00253 // Becoming unavailable after available period 00254 status = false; 00255 if (forward) 00256 { 00257 // Forward 00258 TimePeriod delta = curdate - thedate; 00259 if (delta >= curduration) 00260 { 00261 result.setEnd(thedate + curduration); 00262 break; 00263 } 00264 else 00265 curduration -= delta; 00266 } 00267 else 00268 { 00269 // Backward 00270 TimePeriod delta = thedate - curdate; 00271 if (delta >= curduration) 00272 { 00273 result.setStart(thedate - curduration); 00274 break; 00275 } 00276 else 00277 curduration -= delta; 00278 } 00279 } 00280 else if (forward && curdate == Date::infiniteFuture) 00281 { 00282 // End of forward iteration 00283 if (available) 00284 { 00285 TimePeriod delta = curdate - thedate; 00286 if (delta >= curduration) 00287 result.setEnd(thedate + curduration); 00288 else if (actualduration) 00289 *actualduration = duration - curduration; 00290 } 00291 else if (actualduration) 00292 *actualduration = duration - curduration; 00293 break; 00294 } 00295 else if (!forward && curdate == Date::infinitePast) 00296 { 00297 // End of backward iteration 00298 if (available) 00299 { 00300 TimePeriod delta = thedate - curdate; 00301 if (delta >= curduration) 00302 result.setStart(thedate - curduration); 00303 else if (actualduration) 00304 *actualduration = duration - curduration; 00305 } 00306 else if (actualduration) 00307 *actualduration = duration - curduration; 00308 break; 00309 } 00310 00311 // Advance to the next event 00312 if (forward) ++(*cals[0]); 00313 else --(*cals[0]); 00314 } 00315 00316 // Step 3: Clean up 00317 while (calcount) delete cals[--calcount]; 00318 return result; 00319 } 00320 catch (...) 00321 { 00322 // Clean up 00323 while (calcount) delete cals[calcount--]; 00324 // Rethrow the exception 00325 throw; 00326 } 00327 } 00328 00329 00330 DECLARE_EXPORT DateRange Operation::calculateOperationTime 00331 (Date start, Date end, TimePeriod *actualduration) const 00332 { 00333 // Switch start and end if required 00334 if (end < start) 00335 { 00336 Date tmp = start; 00337 start = end; 00338 end = tmp; 00339 } 00340 00341 int calcount = 0; 00342 // Initial size of 10 should do for 99.99% of all cases 00343 vector<Calendar::EventIterator*> cals(10); 00344 00345 // Default actual duration 00346 if (actualduration) *actualduration = 0L; 00347 00348 try 00349 { 00350 // Step 1: Create an iterator on each of the calendars 00351 // a) operation's location 00352 if (loc && loc->getAvailable()) 00353 cals[calcount++] = new Calendar::EventIterator(loc->getAvailable(), start); 00354 /* @todo multiple availability calendars are not implmented yet 00355 for (Operation::loadlist::const_iterator g=loaddata.begin(); 00356 g!=loaddata.end(); ++g) 00357 { 00358 Resource* res = g->getResource(); 00359 if (res->getMaximum()) 00360 // b) resource size calendar 00361 cals[calcount++] = new Calendar::EventIterator( 00362 res->getMaximum(), 00363 start 00364 ); 00365 if (res->getLocation() && res->getLocation()->getAvailable()) 00366 // c) resource location 00367 cals[calcount++] = new Calendar::EventIterator( 00368 res->getLocation()->getAvailable(), 00369 start 00370 ); 00371 } 00372 */ 00373 00374 // Special case: no calendars at all 00375 if (calcount == 0) 00376 { 00377 if (actualduration) *actualduration = end - start; 00378 return DateRange(start, end); 00379 } 00380 00381 // Step 2: Iterate over the calendar dates to find periods where all 00382 // calendars are simultaneously effective. 00383 DateRange result; 00384 Date curdate = start; 00385 bool status = false; 00386 while (true) 00387 { 00388 // Check whether all calendar are available 00389 bool available = true; 00390 for (int c = 0; c < calcount && available; c++) 00391 { 00392 if (cals[c]->getBucket()) 00393 available = cals[c]->getBucket()->getBool(); 00394 else 00395 available = cals[c]->getCalendar()->getBool(); 00396 } 00397 curdate = cals[0]->getDate(); 00398 00399 if (available && !status) 00400 { 00401 // Becoming available after unavailable period 00402 if (curdate >= end) 00403 { 00404 // Leaving the desired date range 00405 result.setEnd(start); 00406 break; 00407 } 00408 start = curdate; 00409 status = true; 00410 if (result.getStart() == Date::infinitePast) 00411 // First available time - make operation start at this time 00412 result.setStart(curdate); 00413 } 00414 else if (!available && status) 00415 { 00416 // Becoming unavailable after available period 00417 if (curdate >= end) 00418 { 00419 // Leaving the desired date range 00420 if (actualduration) *actualduration += end - start; 00421 result.setEnd(end); 00422 break; 00423 } 00424 status = false; 00425 if (actualduration) *actualduration += curdate - start; 00426 start = curdate; 00427 } 00428 else if (curdate >= end) 00429 { 00430 // Leaving the desired date range 00431 if (available) 00432 { 00433 if (actualduration) *actualduration += end - start; 00434 result.setEnd(end); 00435 break; 00436 } 00437 else 00438 result.setEnd(start); 00439 break; 00440 } 00441 00442 // Advance to the next event 00443 ++(*cals[0]); 00444 } 00445 00446 // Step 3: Clean up 00447 while (calcount) delete cals[--calcount]; 00448 return result; 00449 } 00450 catch (...) 00451 { 00452 // Clean up 00453 while (calcount) delete cals[calcount--]; 00454 // Rethrow the exception 00455 throw; 00456 } 00457 } 00458 00459 00460 DECLARE_EXPORT void Operation::initOperationPlan (OperationPlan* opplan, 00461 double q, const Date& s, const Date& e, Demand* l, OperationPlan* ow, 00462 unsigned long i, bool makeflowsloads) const 00463 { 00464 opplan->oper = const_cast<Operation*>(this); 00465 opplan->setDemand(l); 00466 opplan->id = i; 00467 00468 // Setting the owner first. Note that the order is important here! 00469 // For alternates & routings the quantity needs to be set through the owner. 00470 opplan->setOwner(ow); 00471 00472 // Setting the dates and quantity 00473 setOperationPlanParameters(opplan,q,s,e); 00474 00475 // Create the loadplans and flowplans, if allowed 00476 if (makeflowsloads) opplan->createFlowLoads(); 00477 00478 // Update flow and loadplans, and mark for problem detection 00479 opplan->update(); 00480 } 00481 00482 00483 DECLARE_EXPORT void Operation::deleteOperationPlans(bool deleteLockedOpplans) 00484 { 00485 OperationPlan::deleteOperationPlans(this, deleteLockedOpplans); 00486 } 00487 00488 00489 DECLARE_EXPORT void Operation::writeElement(XMLOutput *o, const Keyword& tag, mode m) const 00490 { 00491 // Note that this class is abstract and never instantiated directly. There is 00492 // therefore no reason to ever write a header. 00493 assert(m == NOHEADER); 00494 00495 // Write the fields 00496 HasDescription::writeElement(o, tag); 00497 Plannable::writeElement(o, tag); 00498 if (post_time) 00499 o->writeElement(Tags::tag_posttime, post_time); 00500 if (pre_time) 00501 o->writeElement(Tags::tag_pretime, pre_time); 00502 if (getCost() != 0.0) 00503 o->writeElement(Tags::tag_cost, getCost()); 00504 if (fence) 00505 o->writeElement(Tags::tag_fence, fence); 00506 if (size_minimum != 1.0) 00507 o->writeElement(Tags::tag_size_minimum, size_minimum); 00508 if (size_multiple > 0.0) 00509 o->writeElement(Tags::tag_size_multiple, size_multiple); 00510 if (size_maximum < DBL_MAX) 00511 o->writeElement(Tags::tag_size_maximum, size_maximum); 00512 if (loc) 00513 o->writeElement(Tags::tag_location, loc); 00514 00515 // Write extra plan information 00516 if ((o->getContentType() == XMLOutput::PLAN 00517 || o->getContentType() == XMLOutput::PLANDETAIL) && first_opplan) 00518 { 00519 o->BeginObject(Tags::tag_operationplans); 00520 for (OperationPlan::iterator i(this); i!=OperationPlan::end(); ++i) 00521 o->writeElement(Tags::tag_operationplan, *i, FULL); 00522 o->EndObject(Tags::tag_operationplans); 00523 } 00524 } 00525 00526 00527 DECLARE_EXPORT void Operation::beginElement(XMLInput& pIn, const Attribute& pAttr) 00528 { 00529 if (pAttr.isA(Tags::tag_flow) 00530 && pIn.getParentElement().first.isA(Tags::tag_flows)) 00531 { 00532 Flow *f = 00533 dynamic_cast<Flow*>(MetaCategory::ControllerDefault(Flow::metadata,pIn.getAttributes())); 00534 if (f) f->setOperation(this); 00535 pIn.readto(f); 00536 } 00537 else if (pAttr.isA (Tags::tag_load) 00538 && pIn.getParentElement().first.isA(Tags::tag_loads)) 00539 { 00540 Load* l = new Load(); 00541 l->setOperation(this); 00542 pIn.readto(&*l); 00543 } 00544 else if (pAttr.isA (Tags::tag_operationplan)) 00545 pIn.readto(OperationPlan::createOperationPlan(OperationPlan::metadata, pIn.getAttributes())); 00546 else if (pAttr.isA (Tags::tag_location)) 00547 pIn.readto( Location::reader(Location::metadata,pIn.getAttributes()) ); 00548 } 00549 00550 00551 DECLARE_EXPORT void Operation::endElement (XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement) 00552 { 00553 if (pAttr.isA (Tags::tag_fence)) 00554 setFence(pElement.getTimeperiod()); 00555 else if (pAttr.isA (Tags::tag_size_minimum)) 00556 setSizeMinimum(pElement.getDouble()); 00557 else if (pAttr.isA (Tags::tag_cost)) 00558 setCost(pElement.getDouble()); 00559 else if (pAttr.isA (Tags::tag_size_multiple)) 00560 setSizeMultiple(pElement.getDouble()); 00561 else if (pAttr.isA (Tags::tag_size_maximum)) 00562 setSizeMaximum(pElement.getDouble()); 00563 else if (pAttr.isA (Tags::tag_pretime)) 00564 setPreTime(pElement.getTimeperiod()); 00565 else if (pAttr.isA (Tags::tag_posttime)) 00566 setPostTime(pElement.getTimeperiod()); 00567 else if (pAttr.isA (Tags::tag_location)) 00568 { 00569 Location *l = dynamic_cast<Location*>(pIn.getPreviousObject()); 00570 if (l) setLocation(l); 00571 else throw LogicException("Incorrect object type during read operation"); 00572 } 00573 else 00574 { 00575 Plannable::endElement(pIn, pAttr, pElement); 00576 HasDescription::endElement(pIn, pAttr, pElement); 00577 } 00578 } 00579 00580 00581 DECLARE_EXPORT OperationPlanState 00582 OperationFixedTime::setOperationPlanParameters 00583 (OperationPlan* opplan, double q, Date s, Date e, bool preferEnd, bool execute) const 00584 { 00585 // Invalid call to the function, or locked operationplan. 00586 if (!opplan || q<0) 00587 throw LogicException("Incorrect parameters for fixedtime operationplan"); 00588 if (opplan->getLocked()) 00589 return OperationPlanState(opplan); 00590 00591 // All quantities are valid, as long as they are above the minimum size and 00592 // below the maximum size 00593 if (q > 0 && q < getSizeMinimum()) q = getSizeMinimum(); 00594 if (q > getSizeMaximum()) q = getSizeMaximum(); 00595 if (fabs(q - opplan->getQuantity()) > ROUNDING_ERROR) 00596 q = opplan->setQuantity(q, false, false, execute); 00597 00598 // Set the start and end date. 00599 DateRange x; 00600 TimePeriod actualduration; 00601 if (e && s) 00602 { 00603 if (preferEnd) x = calculateOperationTime(e, duration, false, &actualduration); 00604 else x = calculateOperationTime(s, duration, true, &actualduration); 00605 } 00606 else if (s) x = calculateOperationTime(s, duration, true, &actualduration); 00607 else x = calculateOperationTime(e, duration, false, &actualduration); 00608 if (!execute) 00609 // Simulation only 00610 return OperationPlanState(x, actualduration == duration ? q : 0); 00611 else if (actualduration == duration) 00612 // Update succeeded 00613 opplan->setStartAndEnd(x.getStart(), x.getEnd()); 00614 else 00615 // Update failed - Not enough available time 00616 opplan->setQuantity(0); 00617 00618 // Return value 00619 return OperationPlanState(opplan); 00620 } 00621 00622 00623 DECLARE_EXPORT bool OperationFixedTime::extraInstantiate(OperationPlan* o) 00624 { 00625 // See if we can consolidate this operationplan with an existing one. 00626 // Merging is possible only when all the following conditions are met: 00627 // - id of the new opplan is not set 00628 // - id of the old opplan is set 00629 // - it is a fixedtime operation 00630 // - it doesn't load any resources 00631 // - both operationplans aren't locked 00632 // - both operationplans have no owner 00633 // - start and end date of both operationplans are the same 00634 // - demand of both operationplans are the same 00635 // - maximum operation size is not exceeded 00636 // - alternate flowplans need to be on the same alternate 00637 if (!o->getIdentifier() && !o->getLocked() && !o->getOwner() && getLoads().empty()) 00638 { 00639 // Loop through candidates 00640 OperationPlan::iterator x(this); 00641 OperationPlan *y = NULL; 00642 for (; x != OperationPlan::end() && *x < *o; ++x) 00643 y = &*x; 00644 if (y && y->getDates() == o->getDates() && !y->getOwner() 00645 && y->getDemand() == o->getDemand() && !y->getLocked() && y->getIdentifier() 00646 && y->getQuantity() + o->getQuantity() < getSizeMaximum()) 00647 { 00648 // Check that the flowplans are on identical alternates 00649 OperationPlan::FlowPlanIterator fp1 = o->beginFlowPlans(); 00650 OperationPlan::FlowPlanIterator fp2 = y->beginFlowPlans(); 00651 while (fp1 != o->endFlowPlans()) 00652 { 00653 if (fp1->getBuffer() != fp2->getBuffer()) 00654 // Different alternates - no merge possible 00655 return true; 00656 ++fp1; 00657 ++fp2; 00658 } 00659 // Merging with the 'next' operationplan 00660 y->setQuantity(y->getQuantity() + o->getQuantity()); 00661 return false; 00662 } 00663 if (x!= OperationPlan::end() && x->getDates() == o->getDates() && !x->getOwner() 00664 && x->getDemand() == o->getDemand() && !x->getLocked() && x->getIdentifier() 00665 && x->getQuantity() + o->getQuantity() < getSizeMaximum()) 00666 { 00667 // Check that the flowplans are on identical alternates 00668 OperationPlan::FlowPlanIterator fp1 = o->beginFlowPlans(); 00669 OperationPlan::FlowPlanIterator fp2 = x->beginFlowPlans(); 00670 while (fp1 != o->endFlowPlans()) 00671 { 00672 if (fp1->getBuffer() != fp2->getBuffer()) 00673 // Different alternates - no merge possible 00674 return true; 00675 ++fp1; 00676 ++fp2; 00677 } 00678 // Merging with the 'previous' operationplan 00679 x->setQuantity(x->getQuantity() + o->getQuantity()); 00680 return false; 00681 } 00682 } 00683 return true; 00684 } 00685 00686 00687 DECLARE_EXPORT void OperationFixedTime::writeElement 00688 (XMLOutput *o, const Keyword& tag, mode m) const 00689 { 00690 // Writing a reference 00691 if (m == REFERENCE) 00692 { 00693 o->writeElement 00694 (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type); 00695 return; 00696 } 00697 00698 // Write the complete object 00699 if (m != NOHEADER) o->BeginObject 00700 (tag, Tags::tag_name, XMLEscape(getName()), Tags::tag_type, getType().type); 00701 00702 // Write the fields 00703 Operation::writeElement(o, tag, NOHEADER); 00704 if (duration) o->writeElement (Tags::tag_duration, duration); 00705 o->EndObject (tag); 00706 } 00707 00708 00709 DECLARE_EXPORT void OperationFixedTime::endElement (XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement) 00710 { 00711 if (pAttr.isA (Tags::tag_duration)) 00712 setDuration (pElement.getTimeperiod()); 00713 else 00714 Operation::endElement (pIn, pAttr, pElement); 00715 } 00716 00717 00718 DECLARE_EXPORT OperationPlanState 00719 OperationTimePer::setOperationPlanParameters 00720 (OperationPlan* opplan, double q, Date s, Date e, bool preferEnd, bool execute) const 00721 { 00722 // Invalid call to the function. 00723 if (!opplan || q<0) 00724 throw LogicException("Incorrect parameters for timeper operationplan"); 00725 if (opplan->getLocked()) 00726 return OperationPlanState(opplan); 00727 00728 // Respect minimum and maximum size 00729 if (q > 0 && q < getSizeMinimum()) q = getSizeMinimum(); 00730 if (q > getSizeMaximum()) q = getSizeMaximum(); 00731 00732 // The logic depends on which dates are being passed along 00733 DateRange x; 00734 TimePeriod actual; 00735 if (s && e) 00736 { 00737 // Case 1: Both the start and end date are specified: Compute the quantity. 00738 // Calculate the available time between those dates 00739 x = calculateOperationTime(s,e,&actual); 00740 if (actual < duration) 00741 { 00742 // Start and end aren't far enough from each other to fit the constant 00743 // part of the operation duration. This is infeasible. 00744 if (!execute) return OperationPlanState(x,0); 00745 opplan->setQuantity(0,true,false,execute); 00746 opplan->setEnd(e); 00747 } 00748 else 00749 { 00750 // Calculate the quantity, respecting minimum, maximum and multiple size. 00751 if (duration_per) 00752 { 00753 if (q * duration_per < static_cast<double>(actual - duration) + 1) 00754 // Provided quantity is acceptable. 00755 // Note that we allow a margin of 1 second to accept. 00756 q = opplan->setQuantity(q, true, false, execute); 00757 else 00758 // Calculate the maximum operationplan that will fit in the window 00759 q = opplan->setQuantity( 00760 static_cast<double>(actual - duration) / duration_per, 00761 true, false, execute); 00762 } 00763 else 00764 // No duration_per field given, so any quantity will go 00765 q = opplan->setQuantity(q, true, false, execute); 00766 00767 // Updates the dates 00768 TimePeriod wanted( 00769 duration + static_cast<long>(duration_per * q) 00770 ); 00771 if (preferEnd) x = calculateOperationTime(e, wanted, false, &actual); 00772 else x = calculateOperationTime(s, wanted, true, &actual); 00773 if (!execute) return OperationPlanState(x,q); 00774 opplan->setStartAndEnd(x.getStart(),x.getEnd()); 00775 } 00776 } 00777 else if (e || !s) 00778 { 00779 // Case 2: Only an end date is specified. Respect the quantity and 00780 // compute the start date 00781 // Case 4: No date was given at all. Respect the quantity and the 00782 // existing end date of the operationplan. 00783 q = opplan->setQuantity(q,true,false,execute); // Round and size the quantity 00784 TimePeriod wanted(duration + static_cast<long>(duration_per * q)); 00785 x = calculateOperationTime(e, wanted, false, &actual); 00786 if (actual == wanted) 00787 { 00788 // Size is as desired 00789 if (!execute) return OperationPlanState(x, q); 00790 opplan->setStartAndEnd(x.getStart(),x.getEnd()); 00791 } 00792 else if (actual < duration) 00793 { 00794 // Not feasible 00795 if (!execute) return OperationPlanState(x, 0); 00796 opplan->setQuantity(0,true,false); 00797 opplan->setStartAndEnd(e,e); 00798 } 00799 else 00800 { 00801 // Resize the quantity to be feasible 00802 double max_q = duration_per ? 00803 static_cast<double>(actual-duration) / duration_per : 00804 q; 00805 q = opplan->setQuantity(q < max_q ? q : max_q, true, false, execute); 00806 wanted = duration + static_cast<long>(duration_per * q); 00807 x = calculateOperationTime(e, wanted, false, &actual); 00808 if (!execute) return OperationPlanState(x, q); 00809 opplan->setStartAndEnd(x.getStart(),x.getEnd()); 00810 } 00811 } 00812 else 00813 { 00814 // Case 3: Only a start date is specified. Respect the quantity and 00815 // compute the end date 00816 q = opplan->setQuantity(q,true,false,execute); // Round and size the quantity 00817 TimePeriod wanted( 00818 duration + static_cast<long>(duration_per * q) 00819 ); 00820 TimePeriod actual; 00821 x = calculateOperationTime(s, wanted, true, &actual); 00822 if (actual == wanted) 00823 { 00824 // Size is as desired 00825 if (!execute) return OperationPlanState(x, q); 00826 opplan->setStartAndEnd(x.getStart(),x.getEnd()); 00827 } 00828 else if (actual < duration) 00829 { 00830 // Not feasible 00831 if (!execute) return OperationPlanState(x, 0); 00832 opplan->setQuantity(0,true,false); 00833 opplan->setStartAndEnd(s,s); 00834 } 00835 else 00836 { 00837 // Resize the quantity to be feasible 00838 double max_q = duration_per ? 00839 static_cast<double>(actual-duration) / duration_per : 00840 q; 00841 q = opplan->setQuantity(q < max_q ? q : max_q, true, false, execute); 00842 wanted = duration + static_cast<long>(duration_per * q); 00843 x = calculateOperationTime(e, wanted, false, &actual); 00844 if (!execute) return OperationPlanState(x, q); 00845 opplan->setStartAndEnd(x.getStart(),x.getEnd()); 00846 } 00847 } 00848 00849 // Return value 00850 return OperationPlanState(opplan); 00851 } 00852 00853 00854 DECLARE_EXPORT void OperationTimePer::writeElement 00855 (XMLOutput *o, const Keyword& tag, mode m) const 00856 { 00857 // Writing a reference 00858 if (m == REFERENCE) 00859 { 00860 o->writeElement 00861 (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type); 00862 return; 00863 } 00864 00865 // Write the complete object 00866 if (m != NOHEADER) o->BeginObject 00867 (tag, Tags::tag_name, XMLEscape(getName()), Tags::tag_type, getType().type); 00868 00869 // Write the complete object 00870 Operation::writeElement(o, tag, NOHEADER); 00871 o->writeElement(Tags::tag_duration, duration); 00872 o->writeElement(Tags::tag_duration_per, duration_per); 00873 o->EndObject(tag); 00874 } 00875 00876 00877 DECLARE_EXPORT void OperationTimePer::endElement (XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement) 00878 { 00879 if (pAttr.isA (Tags::tag_duration)) 00880 setDuration (pElement.getTimeperiod()); 00881 else if (pAttr.isA (Tags::tag_duration_per)) 00882 setDurationPer (pElement.getTimeperiod()); 00883 else 00884 Operation::endElement (pIn, pAttr, pElement); 00885 } 00886 00887 00888 DECLARE_EXPORT void OperationRouting::writeElement 00889 (XMLOutput *o, const Keyword& tag, mode m) const 00890 { 00891 // Writing a reference 00892 if (m == REFERENCE) 00893 { 00894 o->writeElement 00895 (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type); 00896 return; 00897 } 00898 00899 // Write the complete object 00900 if (m != NOHEADER) o->BeginObject 00901 (tag, Tags::tag_name, XMLEscape(getName()), Tags::tag_type, getType().type); 00902 00903 // Write the fields 00904 Operation::writeElement(o, tag, NOHEADER); 00905 if (steps.size()) 00906 { 00907 o->BeginObject(Tags::tag_steps); 00908 for (Operationlist::const_iterator i = steps.begin(); i!=steps.end(); ++i) 00909 o->writeElement(Tags::tag_operation, *i, REFERENCE); 00910 o->EndObject(Tags::tag_steps); 00911 } 00912 o->EndObject(tag); 00913 } 00914 00915 00916 DECLARE_EXPORT void OperationRouting::beginElement(XMLInput& pIn, const Attribute& pAttr) 00917 { 00918 if (pAttr.isA (Tags::tag_operation)) 00919 pIn.readto( Operation::reader(Operation::metadata,pIn.getAttributes()) ); 00920 else 00921 Operation::beginElement(pIn, pAttr); 00922 } 00923 00924 00925 DECLARE_EXPORT void OperationRouting::endElement (XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement) 00926 { 00927 if (pAttr.isA (Tags::tag_operation) 00928 && pIn.getParentElement().first.isA(Tags::tag_steps)) 00929 { 00930 Operation *oper = dynamic_cast<Operation*>(pIn.getPreviousObject()); 00931 if (oper) addStepBack (oper); 00932 else throw LogicException("Incorrect object type during read operation"); 00933 } 00934 Operation::endElement (pIn, pAttr, pElement); 00935 } 00936 00937 00938 DECLARE_EXPORT OperationPlanState OperationRouting::setOperationPlanParameters 00939 (OperationPlan* opplan, double q, Date s, Date e, bool preferEnd, bool execute) const 00940 { 00941 // Invalid call to the function 00942 if (!opplan || q<0) 00943 throw LogicException("Incorrect parameters for routing operationplan"); 00944 if (opplan->getLocked()) 00945 return OperationPlanState(opplan); 00946 00947 if (!opplan->lastsubopplan || opplan->lastsubopplan->getOperation() == OperationSetup::setupoperation) // @todo replace with proper iterator 00948 { 00949 // No step operationplans to work with. Just apply the requested quantity 00950 // and dates. 00951 q = opplan->setQuantity(q,false,false,execute); 00952 if (!s && e) s = e; 00953 if (s && !e) e = s; 00954 if (!execute) return OperationPlanState(s, e, q); 00955 opplan->setStartAndEnd(s,e); 00956 return OperationPlanState(opplan); 00957 } 00958 00959 // Behavior depends on the dates being passed. 00960 // Move all sub-operationplans in an orderly fashion, either starting from 00961 // the specified end date or the specified start date. 00962 OperationPlanState x; 00963 Date y; 00964 bool realfirst = true; 00965 if (e) 00966 { 00967 // Case 1: an end date is specified 00968 for (OperationPlan* i = opplan->lastsubopplan; i; i = i->prevsubopplan) 00969 { 00970 if (i->getOperation() == OperationSetup::setupoperation) continue; 00971 x = i->getOperation()->setOperationPlanParameters(i,q,Date::infinitePast,e,preferEnd,execute); 00972 e = x.start; 00973 if (realfirst) 00974 { 00975 y = x.end; 00976 realfirst = false; 00977 } 00978 } 00979 return OperationPlanState(x.start, y, x.quantity); 00980 } 00981 else if (s) 00982 { 00983 // Case 2: a start date is specified 00984 for (OperationPlan *i = opplan->firstsubopplan; i; i = i->nextsubopplan) 00985 { 00986 if (i->getOperation() == OperationSetup::setupoperation) continue; 00987 x = i->getOperation()->setOperationPlanParameters(i,q,s,Date::infinitePast,preferEnd,execute); 00988 s = x.end; 00989 if (realfirst) 00990 { 00991 y = x.start; 00992 realfirst = false; 00993 } 00994 } 00995 return OperationPlanState(y, x.end, x.quantity); 00996 } 00997 else 00998 throw LogicException( 00999 "Updating a routing operationplan without start or end date argument" 01000 ); 01001 } 01002 01003 01004 DECLARE_EXPORT bool OperationRouting::extraInstantiate(OperationPlan* o) 01005 { 01006 // Create step suboperationplans if they don't exist yet. 01007 if (!o->lastsubopplan || o->lastsubopplan->getOperation() == OperationSetup::setupoperation) 01008 { 01009 Date d = o->getDates().getEnd(); 01010 OperationPlan *p = NULL; 01011 // @todo not possible to initialize a routing oplan based on a start date 01012 if (d != Date::infiniteFuture) 01013 { 01014 // Using the end date 01015 for (Operation::Operationlist::const_reverse_iterator e = 01016 getSubOperations().rbegin(); e != getSubOperations().rend(); ++e) 01017 { 01018 p = (*e)->createOperationPlan(o->getQuantity(), Date::infinitePast, 01019 d, NULL, o, 0, true); 01020 d = p->getDates().getStart(); 01021 } 01022 } 01023 else 01024 { 01025 // Using the start date when there is no end date 01026 d = o->getDates().getStart(); 01027 // Using the current date when both the start and end date are missing 01028 if (!d) d = Plan::instance().getCurrent(); 01029 for (Operation::Operationlist::const_iterator e = 01030 getSubOperations().begin(); e != getSubOperations().end(); ++e) 01031 { 01032 p = (*e)->createOperationPlan(o->getQuantity(), d, 01033 Date::infinitePast, NULL, o, 0, true); 01034 d = p->getDates().getEnd(); 01035 } 01036 } 01037 } 01038 return true; 01039 } 01040 01041 01042 DECLARE_EXPORT SearchMode decodeSearchMode(const string& c) 01043 { 01044 if (c == "PRIORITY") return PRIORITY; 01045 if (c == "MINCOST") return MINCOST; 01046 if (c == "MINPENALTY") return MINPENALTY; 01047 if (c == "MINCOSTPENALTY") return MINCOSTPENALTY; 01048 throw DataException("Invalid search mode " + c); 01049 } 01050 01051 01052 DECLARE_EXPORT void OperationAlternate::addAlternate 01053 (Operation* o, int prio, DateRange eff) 01054 { 01055 if (!o) return; 01056 Operationlist::iterator altIter = alternates.begin(); 01057 alternatePropertyList::iterator propIter = alternateProperties.begin(); 01058 while (altIter!=alternates.end() && prio >= propIter->first) 01059 { 01060 ++propIter; 01061 ++altIter; 01062 } 01063 alternateProperties.insert(propIter,alternateProperty(prio,eff)); 01064 alternates.insert(altIter,o); 01065 o->addSuperOperation(this); 01066 } 01067 01068 01069 DECLARE_EXPORT const OperationAlternate::alternateProperty& 01070 OperationAlternate::getProperties(Operation* o) const 01071 { 01072 if (!o) 01073 throw LogicException("Null pointer passed when searching for a \ 01074 suboperation of alternate operation '" + getName() + "'"); 01075 Operationlist::const_iterator altIter = alternates.begin(); 01076 alternatePropertyList::const_iterator propIter = alternateProperties.begin(); 01077 while (altIter!=alternates.end() && *altIter != o) 01078 { 01079 ++propIter; 01080 ++altIter; 01081 } 01082 if (*altIter == o) return *propIter; 01083 throw DataException("Operation '" + o->getName() + 01084 "' isn't a suboperation of alternate operation '" + getName() + "'"); 01085 } 01086 01087 01088 DECLARE_EXPORT void OperationAlternate::setPriority(Operation* o, int f) 01089 { 01090 if (!o) return; 01091 Operationlist::const_iterator altIter = alternates.begin(); 01092 alternatePropertyList::iterator propIter = alternateProperties.begin(); 01093 while (altIter!=alternates.end() && *altIter != o) 01094 { 01095 ++propIter; 01096 ++altIter; 01097 } 01098 if (*altIter == o) 01099 propIter->first = f; 01100 else 01101 throw DataException("Operation '" + o->getName() + 01102 "' isn't a suboperation of alternate operation '" + getName() + "'"); 01103 } 01104 01105 01106 DECLARE_EXPORT void OperationAlternate::setEffective(Operation* o, DateRange dr) 01107 { 01108 if (!o) return; 01109 Operationlist::const_iterator altIter = alternates.begin(); 01110 alternatePropertyList::iterator propIter = alternateProperties.begin(); 01111 while (altIter!=alternates.end() && *altIter != o) 01112 { 01113 ++propIter; 01114 ++altIter; 01115 } 01116 if (*altIter == o) 01117 propIter->second = dr; 01118 else 01119 throw DataException("Operation '" + o->getName() + 01120 "' isn't a suboperation of alternate operation '" + getName() + "'"); 01121 } 01122 01123 01124 DECLARE_EXPORT void OperationAlternate::writeElement 01125 (XMLOutput *o, const Keyword& tag, mode m) const 01126 { 01127 // Writing a reference 01128 if (m == REFERENCE) 01129 { 01130 o->writeElement 01131 (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type); 01132 return; 01133 } 01134 01135 // Write the complete object 01136 if (m != NOHEADER) o->BeginObject 01137 (tag, Tags::tag_name, XMLEscape(getName()), Tags::tag_type, getType().type); 01138 01139 // Write the standard fields 01140 Operation::writeElement(o, tag, NOHEADER); 01141 if (search != PRIORITY) 01142 o->writeElement(Tags::tag_search, search); 01143 01144 // Write the extra fields 01145 o->BeginObject(Tags::tag_alternates); 01146 alternatePropertyList::const_iterator propIter = alternateProperties.begin(); 01147 for (Operationlist::const_iterator i = alternates.begin(); 01148 i != alternates.end(); ++i) 01149 { 01150 o->BeginObject(Tags::tag_alternate); 01151 o->writeElement(Tags::tag_operation, *i, REFERENCE); 01152 o->writeElement(Tags::tag_priority, propIter->first); 01153 if (propIter->second.getStart() != Date::infinitePast) 01154 o->writeElement(Tags::tag_effective_start, propIter->second.getStart()); 01155 if (propIter->second.getEnd() != Date::infiniteFuture) 01156 o->writeElement(Tags::tag_effective_end, propIter->second.getEnd()); 01157 o->EndObject (Tags::tag_alternate); 01158 ++propIter; 01159 } 01160 o->EndObject(Tags::tag_alternates); 01161 01162 // Ending tag 01163 o->EndObject(tag); 01164 } 01165 01166 01167 DECLARE_EXPORT void OperationAlternate::beginElement(XMLInput& pIn, const Attribute& pAttr) 01168 { 01169 if (pAttr.isA(Tags::tag_operation)) 01170 pIn.readto( Operation::reader(Operation::metadata,pIn.getAttributes()) ); 01171 else 01172 Operation::beginElement(pIn, pAttr); 01173 } 01174 01175 01176 DECLARE_EXPORT void OperationAlternate::endElement (XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement) 01177 { 01178 // Saving some typing... 01179 typedef pair<Operation*,alternateProperty> tempData; 01180 01181 // Create a temporary object 01182 if (!pIn.getUserArea()) 01183 pIn.setUserArea(new tempData(static_cast<Operation*>(NULL),alternateProperty(1,DateRange()))); 01184 tempData* tmp = static_cast<tempData*>(pIn.getUserArea()); 01185 01186 if (pAttr.isA(Tags::tag_alternate)) 01187 { 01188 addAlternate(tmp->first, tmp->second.first, tmp->second.second); 01189 // Reset the defaults 01190 tmp->first = NULL; 01191 tmp->second.first = 1; 01192 tmp->second.second = DateRange(); 01193 } 01194 else if (pAttr.isA(Tags::tag_priority)) 01195 tmp->second.first = pElement.getInt(); 01196 else if (pAttr.isA(Tags::tag_search)) 01197 setSearch(pElement.getString()); 01198 else if (pAttr.isA(Tags::tag_effective_start)) 01199 tmp->second.second.setStart(pElement.getDate()); 01200 else if (pAttr.isA(Tags::tag_effective_end)) 01201 tmp->second.second.setEnd(pElement.getDate()); 01202 else if (pAttr.isA(Tags::tag_operation) 01203 && pIn.getParentElement().first.isA(Tags::tag_alternate)) 01204 { 01205 Operation * b = dynamic_cast<Operation*>(pIn.getPreviousObject()); 01206 if (b) tmp->first = b; 01207 else throw LogicException("Incorrect object type during read operation"); 01208 } 01209 Operation::endElement (pIn, pAttr, pElement); 01210 01211 // Delete the temporary object 01212 if (pIn.isObjectEnd()) delete static_cast<tempData*>(pIn.getUserArea()); 01213 } 01214 01215 01216 DECLARE_EXPORT OperationPlanState 01217 OperationAlternate::setOperationPlanParameters 01218 (OperationPlan* opplan, double q, Date s, Date e, bool preferEnd, 01219 bool execute) const 01220 { 01221 // Invalid calls to this function 01222 if (!opplan || q<0) 01223 throw LogicException("Incorrect parameters for alternate operationplan"); 01224 if (opplan->getLocked()) 01225 return OperationPlanState(opplan); 01226 01227 OperationPlan *x = opplan->lastsubopplan; 01228 while (x && x->getOperation() == OperationSetup::setupoperation) 01229 x = x->prevsubopplan; 01230 if (!x) 01231 { 01232 // Blindly accept the parameters if there is no suboperationplan 01233 if (execute) 01234 { 01235 opplan->setQuantity(q,false,false); 01236 opplan->setStartAndEnd(s, e); 01237 return OperationPlanState(opplan); 01238 } 01239 else 01240 return OperationPlanState(s, e, opplan->setQuantity(q,false,false,false)); 01241 } 01242 else 01243 // Pass the call to the sub-operation 01244 return x->getOperation() 01245 ->setOperationPlanParameters(x,q,s,e,preferEnd, execute); 01246 } 01247 01248 01249 DECLARE_EXPORT bool OperationAlternate::extraInstantiate(OperationPlan* o) 01250 { 01251 // Create a suboperationplan if one doesn't exist yet. 01252 // We use the first effective alternate by default. 01253 if (!o->lastsubopplan || o->lastsubopplan->getOperation() == OperationSetup::setupoperation) 01254 { 01255 // Find the right operation 01256 Operationlist::const_iterator altIter = getSubOperations().begin(); 01257 for (; altIter != getSubOperations().end(); ) 01258 { 01259 const OperationAlternate::alternateProperty& props = getProperties(*altIter); 01260 // Filter out alternates that are not suitable 01261 if (props.first != 0.0 && props.second.within(o->getDates().getEnd())) 01262 break; 01263 } 01264 if (altIter != getSubOperations().end()) 01265 // Create an operationplan instance 01266 (*altIter)->createOperationPlan( 01267 o->getQuantity(), o->getDates().getStart(), 01268 o->getDates().getEnd(), NULL, o, 0, true); 01269 } 01270 return true; 01271 } 01272 01273 01274 DECLARE_EXPORT void OperationAlternate::removeSubOperation(Operation *o) 01275 { 01276 Operationlist::iterator altIter = alternates.begin(); 01277 alternatePropertyList::iterator propIter = alternateProperties.begin(); 01278 while (altIter!=alternates.end() && *altIter != o) 01279 { 01280 ++propIter; 01281 ++altIter; 01282 } 01283 if (*altIter == o) 01284 { 01285 alternates.erase(altIter); 01286 alternateProperties.erase(propIter); 01287 o->superoplist.remove(this); 01288 setChanged(); 01289 } 01290 else 01291 logger << "Warning: operation '" << *o 01292 << "' isn't a suboperation of alternate operation '" << *this 01293 << "'" << endl; 01294 } 01295 01296 01297 DECLARE_EXPORT OperationPlanState OperationSetup::setOperationPlanParameters 01298 (OperationPlan* opplan, double q, Date s, Date e, bool preferEnd, bool execute) const 01299 { 01300 // Find or create a loadplan 01301 OperationPlan::LoadPlanIterator i = opplan->beginLoadPlans(); 01302 LoadPlan *ldplan = NULL; 01303 if (i != opplan->endLoadPlans()) 01304 // Already exists 01305 ldplan = &*i; 01306 else 01307 { 01308 // Create a new one 01309 if (!opplan->getOwner()) 01310 throw LogicException("Setup operationplan always must have an owner"); 01311 for (loadlist::const_iterator g=opplan->getOwner()->getOperation()->getLoads().begin(); 01312 g!=opplan->getOwner()->getOperation()->getLoads().end(); ++g) 01313 if (g->getResource()->getSetupMatrix() && !g->getSetup().empty()) 01314 { 01315 ldplan = new LoadPlan(opplan, &*g); 01316 break; 01317 } 01318 if (!ldplan) 01319 throw LogicException("Can't find a setup on operation '" 01320 + opplan->getOwner()->getOperation()->getName() + "'"); 01321 } 01322 01323 // Find the setup of the resource at the start of the conversion 01324 const Load* lastld = NULL; 01325 Date boundary = s ? s : e; 01326 if (ldplan->getDate() < boundary) 01327 { 01328 for (TimeLine<LoadPlan>::const_iterator i = ldplan->getResource()->getLoadPlans().begin(ldplan); 01329 i != ldplan->getResource()->getLoadPlans().end() && i->getDate() <= boundary; ++i) 01330 { 01331 const LoadPlan *l = dynamic_cast<const LoadPlan*>(&*i); 01332 if (l && i->getQuantity() != 0.0 01333 && l->getOperationPlan() != opplan 01334 && l->getOperationPlan() != opplan->getOwner() 01335 && !l->getLoad()->getSetup().empty()) 01336 lastld = l->getLoad(); 01337 } 01338 } 01339 if (!lastld) 01340 { 01341 for (TimeLine<LoadPlan>::const_iterator i = ldplan->getResource()->getLoadPlans().begin(ldplan); 01342 i != ldplan->getResource()->getLoadPlans().end(); --i) 01343 { 01344 const LoadPlan *l = dynamic_cast<const LoadPlan*>(&*i); 01345 if (l && i->getQuantity() != 0.0 01346 && l->getOperationPlan() != opplan 01347 && l->getOperationPlan() != opplan->getOwner() 01348 && !l->getLoad()->getSetup().empty() 01349 && l->getDate() <= boundary) 01350 { 01351 lastld = l->getLoad(); 01352 break; 01353 } 01354 } 01355 } 01356 string lastsetup = lastld ? lastld->getSetup() : ldplan->getResource()->getSetup(); 01357 01358 TimePeriod duration(0L); 01359 if (lastsetup != ldplan->getLoad()->getSetup()) 01360 { 01361 // Calculate the setup time 01362 SetupMatrix::Rule *conversionrule = ldplan->getLoad()->getResource()->getSetupMatrix() 01363 ->calculateSetup(lastsetup, ldplan->getLoad()->getSetup()); 01364 duration = conversionrule ? conversionrule->getDuration() : TimePeriod(365L*86400L); 01365 } 01366 01367 // Set the start and end date. 01368 DateRange x; 01369 TimePeriod actualduration; 01370 if (e && s) 01371 { 01372 if (preferEnd) x = calculateOperationTime(e, duration, false, &actualduration); 01373 else x = calculateOperationTime(s, duration, true, &actualduration); 01374 } 01375 else if (s) x = calculateOperationTime(s, duration, true, &actualduration); 01376 else x = calculateOperationTime(e, duration, false, &actualduration); 01377 if (!execute) 01378 // Simulation only 01379 return OperationPlanState(x, actualduration == duration ? q : 0); 01380 else if (actualduration == duration) 01381 { 01382 // Update succeeded 01383 opplan->setStartAndEnd(x.getStart(), x.getEnd()); 01384 if (opplan->getOwner()->getDates().getStart() != opplan->getDates().getEnd()) 01385 opplan->getOwner()->setStart(opplan->getDates().getEnd()); 01386 } 01387 else 01388 // Update failed - Not enough available time @todo setting the qty to 0 is not enough 01389 opplan->setQuantity(0); 01390 01391 return OperationPlanState(opplan); 01392 } 01393 01394 01395 DECLARE_EXPORT PyObject* Operation::getattro(const Attribute& attr) 01396 { 01397 if (attr.isA(Tags::tag_name)) 01398 return PythonObject(getName()); 01399 if (attr.isA(Tags::tag_description)) 01400 return PythonObject(getDescription()); 01401 if (attr.isA(Tags::tag_category)) 01402 return PythonObject(getCategory()); 01403 if (attr.isA(Tags::tag_subcategory)) 01404 return PythonObject(getSubCategory()); 01405 if (attr.isA(Tags::tag_location)) 01406 return PythonObject(getLocation()); 01407 if (attr.isA(Tags::tag_fence)) 01408 return PythonObject(getFence()); 01409 if (attr.isA(Tags::tag_size_minimum)) 01410 return PythonObject(getSizeMinimum()); 01411 if (attr.isA(Tags::tag_size_multiple)) 01412 return PythonObject(getSizeMultiple()); 01413 if (attr.isA(Tags::tag_size_maximum)) 01414 return PythonObject(getSizeMaximum()); 01415 if (attr.isA(Tags::tag_cost)) 01416 return PythonObject(getCost()); 01417 if (attr.isA(Tags::tag_pretime)) 01418 return PythonObject(getPreTime()); 01419 if (attr.isA(Tags::tag_posttime)) 01420 return PythonObject(getPostTime()); 01421 if (attr.isA(Tags::tag_hidden)) 01422 return PythonObject(getHidden()); 01423 if (attr.isA(Tags::tag_loads)) 01424 return new LoadIterator(this); 01425 if (attr.isA(Tags::tag_flows)) 01426 return new FlowIterator(this); 01427 if (attr.isA(Tags::tag_operationplans)) 01428 return new OperationPlanIterator(this); 01429 if (attr.isA(Tags::tag_level)) 01430 return PythonObject(getLevel()); 01431 if (attr.isA(Tags::tag_cluster)) 01432 return PythonObject(getCluster()); 01433 return NULL; 01434 } 01435 01436 01437 DECLARE_EXPORT int Operation::setattro(const Attribute& attr, const PythonObject& field) 01438 { 01439 if (attr.isA(Tags::tag_name)) 01440 setName(field.getString()); 01441 else if (attr.isA(Tags::tag_description)) 01442 setDescription(field.getString()); 01443 else if (attr.isA(Tags::tag_category)) 01444 setCategory(field.getString()); 01445 else if (attr.isA(Tags::tag_subcategory)) 01446 setSubCategory(field.getString()); 01447 else if (attr.isA(Tags::tag_location)) 01448 { 01449 if (!field.check(Location::metadata)) 01450 { 01451 PyErr_SetString(PythonDataException, "buffer location must be of type location"); 01452 return -1; 01453 } 01454 Location* y = static_cast<Location*>(static_cast<PyObject*>(field)); 01455 setLocation(y); 01456 } 01457 else if (attr.isA(Tags::tag_fence)) 01458 setFence(field.getTimeperiod()); 01459 else if (attr.isA(Tags::tag_size_minimum)) 01460 setSizeMinimum(field.getDouble()); 01461 else if (attr.isA(Tags::tag_size_multiple)) 01462 setSizeMultiple(field.getDouble()); 01463 else if (attr.isA(Tags::tag_size_maximum)) 01464 setSizeMaximum(field.getDouble()); 01465 else if (attr.isA(Tags::tag_cost)) 01466 setCost(field.getDouble()); 01467 else if (attr.isA(Tags::tag_pretime)) 01468 setPreTime(field.getTimeperiod()); 01469 else if (attr.isA(Tags::tag_posttime)) 01470 setPostTime(field.getTimeperiod()); 01471 else if (attr.isA(Tags::tag_hidden)) 01472 setHidden(field.getBool()); 01473 else 01474 return -1; // Error 01475 return 0; // OK 01476 } 01477 01478 01479 DECLARE_EXPORT PyObject* OperationFixedTime::getattro(const Attribute& attr) 01480 { 01481 if (attr.isA(Tags::tag_duration)) 01482 return PythonObject(getDuration()); 01483 return Operation::getattro(attr); 01484 } 01485 01486 01487 DECLARE_EXPORT int OperationFixedTime::setattro(const Attribute& attr, const PythonObject& field) 01488 { 01489 if (attr.isA(Tags::tag_duration)) 01490 setDuration(field.getTimeperiod()); 01491 else 01492 return Operation::setattro(attr, field); 01493 return 0; 01494 } 01495 01496 01497 DECLARE_EXPORT PyObject* OperationTimePer::getattro(const Attribute& attr) 01498 { 01499 if (attr.isA(Tags::tag_duration)) 01500 return PythonObject(getDuration()); 01501 if (attr.isA(Tags::tag_duration_per)) 01502 return PythonObject(getDurationPer()); 01503 return Operation::getattro(attr); 01504 } 01505 01506 01507 DECLARE_EXPORT int OperationTimePer::setattro(const Attribute& attr, const PythonObject& field) 01508 { 01509 if (attr.isA(Tags::tag_duration)) 01510 setDuration(field.getTimeperiod()); 01511 else if (attr.isA(Tags::tag_duration_per)) 01512 setDurationPer(field.getTimeperiod()); 01513 else 01514 return Operation::setattro(attr, field); 01515 return 0; 01516 } 01517 01518 01519 DECLARE_EXPORT PyObject* OperationAlternate::getattro(const Attribute& attr) 01520 { 01521 if (attr.isA(Tags::tag_alternates)) 01522 { 01523 PyObject* result = PyTuple_New(getSubOperations().size()); 01524 int count = 0; 01525 for (Operation::Operationlist::const_iterator i = getSubOperations().begin(); i != getSubOperations().end(); ++i) 01526 PyTuple_SetItem(result, count++, PythonObject(*i)); 01527 return result; 01528 } 01529 if (attr.isA(Tags::tag_search)) 01530 { 01531 ostringstream ch; 01532 ch << getSearch(); 01533 return PythonObject(ch.str()); 01534 } 01535 return Operation::getattro(attr); 01536 } 01537 01538 01539 DECLARE_EXPORT int OperationAlternate::setattro(const Attribute& attr, const PythonObject& field) 01540 { 01541 if (attr.isA(Tags::tag_search)) 01542 setSearch(field.getString()); 01543 else 01544 return Operation::setattro(attr, field); 01545 return 0; 01546 } 01547 01548 01549 DECLARE_EXPORT PyObject* OperationAlternate::addAlternate(PyObject* self, PyObject* args, PyObject* kwdict) 01550 { 01551 try 01552 { 01553 // Pick up the alternate operation 01554 OperationAlternate *altoper = static_cast<OperationAlternate*>(self); 01555 if (!altoper) throw LogicException("Can't add alternates to NULL alternate"); 01556 01557 // Parse the arguments 01558 PyObject *oper = NULL; 01559 int prio = 1; 01560 PyObject *eff_start = NULL; 01561 PyObject *eff_end = NULL; 01562 static const char *kwlist[] = {"operation", "priority", "effective_start", "effective_end", NULL}; 01563 if (!PyArg_ParseTupleAndKeywords(args, kwdict, 01564 "O|iOO:addAlternate", 01565 const_cast<char**>(kwlist), &oper, &prio, &eff_start, &eff_end)) 01566 return NULL; 01567 if (!PyObject_TypeCheck(oper, Operation::metadata->pythonClass)) 01568 throw DataException("alternate operation must be of type operation"); 01569 DateRange eff; 01570 if (eff_start) 01571 { 01572 PythonObject d(eff_start); 01573 eff.setStart(d.getDate()); 01574 } 01575 if (eff_end) 01576 { 01577 PythonObject d(eff_end); 01578 eff.setEnd(d.getDate()); 01579 } 01580 01581 // Add the alternate 01582 altoper->addAlternate(static_cast<Operation*>(oper), prio, eff); 01583 } 01584 catch(...) 01585 { 01586 PythonType::evalException(); 01587 return NULL; 01588 } 01589 return Py_BuildValue(""); 01590 } 01591 01592 01593 DECLARE_EXPORT PyObject* OperationRouting::getattro(const Attribute& attr) 01594 { 01595 if (attr.isA(Tags::tag_steps)) 01596 { 01597 PyObject* result = PyTuple_New(getSubOperations().size()); 01598 int count = 0; 01599 for (Operation::Operationlist::const_iterator i = getSubOperations().begin(); i != getSubOperations().end(); ++i) 01600 PyTuple_SetItem(result, count++, PythonObject(*i)); 01601 return result; 01602 } 01603 return Operation::getattro(attr); 01604 } 01605 01606 01607 PyObject *OperationRouting::addStep(PyObject *self, PyObject *args) 01608 { 01609 try 01610 { 01611 // Pick up the routing operation 01612 OperationRouting *oper = static_cast<OperationRouting*>(self); 01613 if (!oper) throw LogicException("Can't add steps to NULL routing"); 01614 01615 // Parse the arguments 01616 PyObject *steps[4]; 01617 for (unsigned int i=0; i<4; ++i) steps[i] = NULL; 01618 if (PyArg_UnpackTuple(args, "addStep", 1, 4, &steps[0], &steps[1], &steps[2], &steps[3])) 01619 for (unsigned int i=0; i<4 && steps[i]; ++i) 01620 { 01621 if (!PyObject_TypeCheck(steps[i], Operation::metadata->pythonClass)) 01622 throw DataException("routing steps must be of type operation"); 01623 oper->addStepBack(static_cast<Operation*>(steps[i])); 01624 } 01625 } 01626 catch(...) 01627 { 01628 PythonType::evalException(); 01629 return NULL; 01630 } 01631 return Py_BuildValue(""); 01632 } 01633 01634 } // end namespace