resource.cpp
Go to the documentation of this file.
00001 /*************************************************************************** 00002 file : $URL: http://svn.code.sf.net/p/frepple/code/trunk/src/model/resource.cpp $ 00003 version : $LastChangedRevision: 1718 $ $LastChangedBy: jdetaeye $ 00004 date : $LastChangedDate: 2012-07-21 10:05:34 +0200 (Sat, 21 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 Resource> DECLARE_EXPORT Tree utils::HasName<Resource>::st; 00034 DECLARE_EXPORT const MetaCategory* Resource::metadata; 00035 DECLARE_EXPORT const MetaClass* ResourceDefault::metadata; 00036 DECLARE_EXPORT const MetaClass* ResourceInfinite::metadata; 00037 00038 00039 int Resource::initialize() 00040 { 00041 // Initialize the metadata 00042 metadata = new MetaCategory("resource", "resources", reader, writer); 00043 00044 // Initialize the Python class 00045 FreppleCategory<Resource>::getType().addMethod("plan", Resource::plan, METH_VARARGS, 00046 "Return an iterator with tuples representing the resource plan in each time bucket"); 00047 return FreppleCategory<Resource>::initialize(); 00048 } 00049 00050 00051 int ResourceDefault::initialize() 00052 { 00053 // Initialize the metadata 00054 ResourceDefault::metadata = new MetaClass( 00055 "resource", 00056 "resource_default", 00057 Object::createString<ResourceDefault>, 00058 true); 00059 00060 // Initialize the Python class 00061 return FreppleClass<ResourceDefault,Resource>::initialize(); 00062 } 00063 00064 00065 int ResourceInfinite::initialize() 00066 { 00067 // Initialize the metadata 00068 ResourceInfinite::metadata = new MetaClass( 00069 "resource", 00070 "resource_infinite", 00071 Object::createString<ResourceInfinite>); 00072 00073 // Initialize the Python class 00074 return FreppleClass<ResourceInfinite,Resource>::initialize(); 00075 } 00076 00077 00078 DECLARE_EXPORT void Resource::setMaximum(double m) 00079 { 00080 if (m < 0) 00081 throw DataException("Maximum capacity for resource '" + getName() + "' must be postive"); 00082 00083 // There is already a maximum calendar. 00084 if (size_max_cal) 00085 { 00086 // We update the field, but don't use it yet. 00087 size_max = m; 00088 return; 00089 } 00090 00091 // Mark as changed 00092 setChanged(); 00093 00094 // Set field 00095 size_max = m; 00096 00097 // Create or update a single timeline max event 00098 for (loadplanlist::iterator oo=loadplans.begin(); oo!=loadplans.end(); oo++) 00099 if (oo->getType() == 4) 00100 { 00101 // Update existing event 00102 static_cast<loadplanlist::EventMaxQuantity *>(&*oo)->setMax(size_max); 00103 return; 00104 } 00105 // Create new event 00106 loadplanlist::EventMaxQuantity *newEvent = 00107 new loadplanlist::EventMaxQuantity(Date::infinitePast, size_max); 00108 loadplans.insert(newEvent); 00109 } 00110 00111 00112 DECLARE_EXPORT void Resource::setMaximumCalendar(CalendarDouble* c) 00113 { 00114 // Resetting the same calendar 00115 if (size_max_cal == c) return; 00116 00117 // Mark as changed 00118 setChanged(); 00119 00120 // Remove the current max events. 00121 for (loadplanlist::iterator oo=loadplans.begin(); oo!=loadplans.end(); ) 00122 if (oo->getType() == 4) 00123 { 00124 loadplans.erase(&(*oo)); 00125 delete &(*(oo++)); 00126 } 00127 else ++oo; 00128 00129 // Null pointer passed. Change back to time independent maximum size. 00130 if (!c) 00131 { 00132 setMaximum(size_max); 00133 return; 00134 } 00135 00136 // Create timeline structures for every bucket. 00137 size_max_cal = c; 00138 double curMax = 0.0; 00139 for (CalendarDouble::EventIterator x(size_max_cal); x.getDate()<Date::infiniteFuture; ++x) 00140 if (curMax != x.getValue()) 00141 { 00142 curMax = x.getValue(); 00143 loadplanlist::EventMaxQuantity *newBucket = 00144 new loadplanlist::EventMaxQuantity(x.getDate(), curMax); 00145 loadplans.insert(newBucket); 00146 } 00147 } 00148 00149 00150 DECLARE_EXPORT void Resource::writeElement(XMLOutput *o, const Keyword& tag, mode m) const 00151 { 00152 // Write a reference 00153 if (m == REFERENCE) 00154 { 00155 o->writeElement(tag, Tags::tag_name, getName()); 00156 return; 00157 } 00158 00159 // Write the complete object 00160 if (m != NOHEADER) o->BeginObject(tag, Tags::tag_name, XMLEscape(getName())); 00161 00162 // Write my fields 00163 HasDescription::writeElement(o, tag); 00164 HasHierarchy<Resource>::writeElement(o, tag); 00165 if (getMaximum() != 1) 00166 o->writeElement(Tags::tag_maximum, getMaximum()); 00167 o->writeElement(Tags::tag_maximum_calendar, size_max_cal); 00168 if (getMaxEarly() != TimePeriod(defaultMaxEarly)) 00169 o->writeElement(Tags::tag_maxearly, getMaxEarly()); 00170 if (getCost() != 0.0) o->writeElement(Tags::tag_cost, getCost()); 00171 o->writeElement(Tags::tag_location, loc); 00172 if (!getSetup().empty()) o->writeElement(Tags::tag_setup, getSetup()); 00173 if (getSetupMatrix()) 00174 o->writeElement(Tags::tag_setupmatrix, getSetupMatrix()); 00175 Plannable::writeElement(o, tag); 00176 00177 // Write extra plan information 00178 loadplanlist::const_iterator i = loadplans.begin(); 00179 if (o->getContentType() == XMLOutput::PLAN && i!=loadplans.end()) 00180 { 00181 o->BeginObject(Tags::tag_loadplans); 00182 for (; i!=loadplans.end(); ++i) 00183 if (i->getType()==1) 00184 { 00185 const LoadPlan *lp = dynamic_cast<const LoadPlan*>(&*i); 00186 o->BeginObject(Tags::tag_loadplan); 00187 o->writeElement(Tags::tag_date, lp->getDate()); 00188 o->writeElement(Tags::tag_quantity, lp->getQuantity()); 00189 o->writeElement(Tags::tag_onhand, lp->getOnhand()); 00190 o->writeElement(Tags::tag_minimum, lp->getMin()); 00191 o->writeElement(Tags::tag_maximum, lp->getMax()); 00192 o->writeElement(Tags::tag_operationplan, &*(lp->getOperationPlan()), FULL); 00193 o->EndObject(Tags::tag_loadplan); 00194 } 00195 o->EndObject(Tags::tag_loadplans); 00196 } 00197 00198 // That was it 00199 o->EndObject(tag); 00200 } 00201 00202 00203 DECLARE_EXPORT void Resource::beginElement(XMLInput& pIn, const Attribute& pAttr) 00204 { 00205 if (pAttr.isA (Tags::tag_load) 00206 && pIn.getParentElement().first.isA(Tags::tag_loads)) 00207 { 00208 Load* l = new Load(); 00209 l->setResource(this); 00210 pIn.readto(&*l); 00211 } 00212 else if (pAttr.isA (Tags::tag_maximum_calendar)) 00213 pIn.readto( Calendar::reader(Calendar::metadata,pIn.getAttributes()) ); 00214 else if (pAttr.isA(Tags::tag_loadplans)) 00215 pIn.IgnoreElement(); 00216 else if (pAttr.isA(Tags::tag_location)) 00217 pIn.readto( Location::reader(Location::metadata,pIn.getAttributes()) ); 00218 else if (pAttr.isA(Tags::tag_setupmatrix)) 00219 pIn.readto( SetupMatrix::reader(SetupMatrix::metadata,pIn.getAttributes()) ); 00220 else 00221 HasHierarchy<Resource>::beginElement(pIn, pAttr); 00222 } 00223 00224 00225 DECLARE_EXPORT void Resource::endElement (XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement) 00226 { 00227 /* Note that while restoring the size, the parent's size is NOT 00228 automatically updated. The getDescription of the 'set_size' function may 00229 suggest this would be the case... */ 00230 if (pAttr.isA (Tags::tag_maximum)) 00231 setMaximum(pElement.getDouble()); 00232 else if (pAttr.isA (Tags::tag_maximum_calendar)) 00233 { 00234 CalendarDouble *c = dynamic_cast<CalendarDouble*>(pIn.getPreviousObject()); 00235 if (c) 00236 setMaximumCalendar(c); 00237 else 00238 { 00239 Calendar *c = dynamic_cast<Calendar*>(pIn.getPreviousObject()); 00240 if (!c) 00241 throw LogicException("Incorrect object type during read operation"); 00242 throw DataException("Calendar '" + c->getName() + 00243 "' has invalid type for use as resource max calendar"); 00244 } 00245 } 00246 else if (pAttr.isA (Tags::tag_maxearly)) 00247 setMaxEarly(pElement.getTimeperiod()); 00248 else if (pAttr.isA (Tags::tag_cost)) 00249 setCost(pElement.getDouble()); 00250 else if (pAttr.isA(Tags::tag_location)) 00251 { 00252 Location * d = dynamic_cast<Location*>(pIn.getPreviousObject()); 00253 if (d) setLocation(d); 00254 else throw LogicException("Incorrect object type during read operation"); 00255 } 00256 else if (pAttr.isA (Tags::tag_setup)) 00257 setSetup(pElement.getString()); 00258 else if (pAttr.isA(Tags::tag_setupmatrix)) 00259 { 00260 SetupMatrix * d = dynamic_cast<SetupMatrix*>(pIn.getPreviousObject()); 00261 if (d) setSetupMatrix(d); 00262 else throw LogicException("Incorrect object type during read operation"); 00263 } 00264 else 00265 { 00266 Plannable::endElement(pIn, pAttr, pElement); 00267 HasDescription::endElement(pIn, pAttr, pElement); 00268 HasHierarchy<Resource>::endElement (pIn, pAttr, pElement); 00269 } 00270 } 00271 00272 00273 DECLARE_EXPORT void Resource::deleteOperationPlans(bool deleteLocked) 00274 { 00275 // Delete the operationplans 00276 for (loadlist::iterator i=loads.begin(); i!=loads.end(); ++i) 00277 OperationPlan::deleteOperationPlans(i->getOperation(),deleteLocked); 00278 00279 // Mark to recompute the problems 00280 setChanged(); 00281 } 00282 00283 00284 DECLARE_EXPORT Resource::~Resource() 00285 { 00286 // Delete all operationplans 00287 // An alternative logic would be to delete only the loadplans for this 00288 // resource and leave the rest of the plan untouched. The currently 00289 // implemented method is way more drastic... 00290 deleteOperationPlans(true); 00291 00292 // The Load objects are automatically deleted by the destructor 00293 // of the Association list class. 00294 } 00295 00296 00297 DECLARE_EXPORT void Resource::updateSetups(const LoadPlan* ldplan) 00298 { 00299 // No updating required this resource 00300 if (!getSetupMatrix() || (ldplan && ldplan->getOperationPlan()->getOperation() != OperationSetup::setupoperation)) 00301 return; 00302 00303 // Update later setup opplans 00304 OperationPlan *opplan = ldplan ? ldplan->getOperationPlan() : NULL; 00305 loadplanlist::const_iterator i = ldplan ? 00306 getLoadPlans().begin(ldplan) : 00307 getLoadPlans().begin(); 00308 string prevsetup = ldplan ? ldplan->getSetup() : getSetup(); 00309 Date latestCheckDate = ldplan ? ldplan->getDate() : Date::infiniteFuture; 00310 for (; i != getLoadPlans().end(); ++i) 00311 { 00312 const LoadPlan* l = dynamic_cast<const LoadPlan*>(&*i); 00313 if (l && !l->getLoad()->getSetup().empty() 00314 && l->getOperationPlan()->getOperation() == OperationSetup::setupoperation 00315 && l->getOperationPlan() != opplan 00316 && !l->isStart()) 00317 { 00318 // Next conversion operation 00319 OperationPlanState x = l->getOperationPlan()->getOperation()->setOperationPlanParameters( 00320 l->getOperationPlan(), 00321 l->getOperationPlan()->getQuantity(), 00322 Date::infinitePast, 00323 l->getOperationPlan()->getDates().getEnd(), 00324 true, 00325 false); 00326 if (x.start != l->getOperationPlan()->getDates().getStart()) 00327 // We need to change a setup plan 00328 l->getOperationPlan()->restore(x); 00329 else if (ldplan && x.start == l->getOperationPlan()->getDates().getStart()) 00330 // We found a setup plan that doesn't need updating. Later setup plans 00331 // won't require updating either 00332 return; 00333 } 00334 } 00335 } 00336 00337 00338 DECLARE_EXPORT void ResourceInfinite::writeElement 00339 (XMLOutput *o, const Keyword &tag, mode m) const 00340 { 00341 // Writing a reference 00342 if (m == REFERENCE) 00343 { 00344 o->writeElement 00345 (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type); 00346 return; 00347 } 00348 00349 // Write the complete object 00350 if (m != NOHEADER) o->BeginObject 00351 (tag, Tags::tag_name, XMLEscape(getName()), Tags::tag_type, getType().type); 00352 00353 // Write the fields 00354 Resource::writeElement(o, tag, NOHEADER); 00355 } 00356 00357 00358 DECLARE_EXPORT PyObject* Resource::getattro(const Attribute& attr) 00359 { 00360 if (attr.isA(Tags::tag_name)) 00361 return PythonObject(getName()); 00362 if (attr.isA(Tags::tag_description)) 00363 return PythonObject(getDescription()); 00364 if (attr.isA(Tags::tag_category)) 00365 return PythonObject(getCategory()); 00366 if (attr.isA(Tags::tag_subcategory)) 00367 return PythonObject(getSubCategory()); 00368 if (attr.isA(Tags::tag_owner)) 00369 return PythonObject(getOwner()); 00370 if (attr.isA(Tags::tag_location)) 00371 return PythonObject(getLocation()); 00372 if (attr.isA(Tags::tag_maximum)) 00373 return PythonObject(getMaximum()); 00374 if (attr.isA(Tags::tag_maximum_calendar)) 00375 return PythonObject(getMaximumCalendar()); 00376 if (attr.isA(Tags::tag_maxearly)) 00377 return PythonObject(getMaxEarly()); 00378 if (attr.isA(Tags::tag_cost)) 00379 return PythonObject(getCost()); 00380 if (attr.isA(Tags::tag_hidden)) 00381 return PythonObject(getHidden()); 00382 if (attr.isA(Tags::tag_loadplans)) 00383 return new LoadPlanIterator(this); 00384 if (attr.isA(Tags::tag_loads)) 00385 return new LoadIterator(this); 00386 if (attr.isA(Tags::tag_setup)) 00387 return PythonObject(getSetup()); 00388 if (attr.isA(Tags::tag_setupmatrix)) 00389 return PythonObject(getSetupMatrix()); 00390 if (attr.isA(Tags::tag_level)) 00391 return PythonObject(getLevel()); 00392 if (attr.isA(Tags::tag_cluster)) 00393 return PythonObject(getCluster()); 00394 if (attr.isA(Tags::tag_members)) 00395 return new ResourceIterator(this); 00396 return NULL; 00397 } 00398 00399 00400 DECLARE_EXPORT int Resource::setattro(const Attribute& attr, const PythonObject& field) 00401 { 00402 if (attr.isA(Tags::tag_name)) 00403 setName(field.getString()); 00404 else if (attr.isA(Tags::tag_description)) 00405 setDescription(field.getString()); 00406 else if (attr.isA(Tags::tag_category)) 00407 setCategory(field.getString()); 00408 else if (attr.isA(Tags::tag_subcategory)) 00409 setSubCategory(field.getString()); 00410 else if (attr.isA(Tags::tag_owner)) 00411 { 00412 if (!field.check(PythonExtension<Resource>::getType())) 00413 { 00414 PyErr_SetString(PythonDataException, "resource owner must be of type resource"); 00415 return -1; 00416 } 00417 Resource* y = static_cast<Resource*>(static_cast<PyObject*>(field)); 00418 setOwner(y); 00419 } 00420 else if (attr.isA(Tags::tag_location)) 00421 { 00422 if (!field.check(Location::metadata)) 00423 { 00424 PyErr_SetString(PythonDataException, "resource location must be of type location"); 00425 return -1; 00426 } 00427 Location* y = static_cast<Location*>(static_cast<PyObject*>(field)); 00428 setLocation(y); 00429 } 00430 else if (attr.isA(Tags::tag_maximum)) 00431 setMaximum(field.getDouble()); 00432 else if (attr.isA(Tags::tag_maximum_calendar)) 00433 { 00434 if (!field.check(CalendarDouble::metadata)) 00435 { 00436 PyErr_SetString(PythonDataException, "resource maximum_calendar must be of type calendar_double"); 00437 return -1; 00438 } 00439 CalendarDouble* y = static_cast<CalendarDouble*>(static_cast<PyObject*>(field)); 00440 setMaximumCalendar(y); 00441 } 00442 else if (attr.isA(Tags::tag_hidden)) 00443 setHidden(field.getBool()); 00444 else if (attr.isA(Tags::tag_cost)) 00445 setCost(field.getDouble()); 00446 else if (attr.isA(Tags::tag_maxearly)) 00447 setMaxEarly(field.getTimeperiod()); 00448 else if (attr.isA(Tags::tag_setup)) 00449 setSetup(field.getString()); 00450 else if (attr.isA(Tags::tag_setupmatrix)) 00451 { 00452 if (!field.check(SetupMatrix::metadata)) 00453 { 00454 PyErr_SetString(PythonDataException, "resource setup_matrix must be of type setup_matrix"); 00455 return -1; 00456 } 00457 SetupMatrix* y = static_cast<SetupMatrix*>(static_cast<PyObject*>(field)); 00458 setSetupMatrix(y); 00459 } 00460 else 00461 return -1; // Error 00462 return 0; // OK 00463 } 00464 00465 00466 extern "C" PyObject* Resource::plan(PyObject *self, PyObject *args) 00467 { 00468 // Get the resource model 00469 Resource* resource = static_cast<Resource*>(self); 00470 00471 // Parse the Python arguments 00472 PyObject* buckets = NULL; 00473 int ok = PyArg_ParseTuple(args, "O:plan", &buckets); 00474 if (!ok) return NULL; 00475 00476 // Validate that the argument supports iteration. 00477 PyObject* iter = PyObject_GetIter(buckets); 00478 if (!iter) 00479 { 00480 PyErr_Format(PyExc_AttributeError,"Argument to resource.plan() must support iteration"); 00481 return NULL; 00482 } 00483 00484 // Return the iterator 00485 return new Resource::PlanIterator(resource, iter); 00486 } 00487 00488 00489 int Resource::PlanIterator::initialize() 00490 { 00491 // Initialize the type 00492 PythonType& x = PythonExtension<Resource::PlanIterator>::getType(); 00493 x.setName("resourceplanIterator"); 00494 x.setDoc("frePPLe iterator for resourceplan"); 00495 x.supportiter(); 00496 return x.typeReady(); 00497 } 00498 00499 00500 Resource::PlanIterator::PlanIterator(Resource* r, PyObject* o) : 00501 res(r), bucketiterator(o), ldplaniter(r ? r->getLoadPlans().begin() : NULL), 00502 cur_setup(0.0), cur_load(0.0), cur_size(0.0), start_date(NULL), end_date(NULL) 00503 { 00504 if (!r) 00505 { 00506 bucketiterator = NULL; 00507 throw LogicException("Creating resource plan iterator for NULL resource"); 00508 } 00509 00510 // Start date of the first bucket 00511 end_date = PyIter_Next(bucketiterator); 00512 if (!end_date) throw LogicException("Expecting at least two dates as argument"); 00513 cur_date = PythonObject(end_date).getDate(); 00514 prev_date = cur_date; 00515 00516 // A flag to remember whether this resource has an unavailability calendar. 00517 hasUnavailability = r->getLocation() && r->getLocation()->getAvailable(); 00518 if (hasUnavailability) 00519 { 00520 unavailableIterator = Calendar::EventIterator(res->getLocation()->getAvailable(), cur_date); 00521 prev_value = unavailableIterator.getBucket() ? 00522 unavailableIterator.getBucket()->getBool() : 00523 res->getLocation()->getAvailable()->getDefault()!=0; 00524 } 00525 00526 // Advance loadplan iterator just beyond the starting date 00527 while (ldplaniter != res->getLoadPlans().end() && ldplaniter->getDate() <= cur_date) 00528 { 00529 if (ldplaniter->getType() == 4) 00530 // New max size 00531 cur_size = ldplaniter->getMax(); 00532 else 00533 { 00534 const LoadPlan* ldplan = dynamic_cast<const LoadPlan*>(&*ldplaniter); 00535 if (!ldplan) continue; 00536 if (ldplan->getOperationPlan()->getOperation() == OperationSetup::setupoperation) 00537 // Setup starting or ending 00538 cur_setup = ldplan->getQuantity() < 0 ? 0.0 : cur_size; 00539 else 00540 // Normal load 00541 cur_load = ldplan->getOnhand(); 00542 } 00543 ++ldplaniter; 00544 } 00545 } 00546 00547 00548 Resource::PlanIterator::~PlanIterator() 00549 { 00550 if (bucketiterator) Py_DECREF(bucketiterator); 00551 if (start_date) Py_DECREF(start_date); 00552 if (end_date) Py_DECREF(end_date); 00553 } 00554 00555 00556 void Resource::PlanIterator::update(Date till) 00557 { 00558 long timedelta; 00559 if (hasUnavailability) 00560 { 00561 // Advance till the iterator exceeds the target date 00562 while (unavailableIterator.getDate() <= till) 00563 { 00564 timedelta = unavailableIterator.getDate() - prev_date; 00565 if (prev_value) 00566 { 00567 bucket_available += cur_size * timedelta; 00568 bucket_load += cur_load * timedelta; 00569 bucket_setup += cur_setup * timedelta; 00570 } 00571 else 00572 bucket_unavailable += cur_size * timedelta; 00573 prev_value = unavailableIterator.getBucket() ? 00574 unavailableIterator.getBucket()->getBool() : 00575 res->getLocation()->getAvailable()->getDefault()!=0; 00576 prev_date = unavailableIterator.getDate(); 00577 ++unavailableIterator; 00578 } 00579 // Account for time period finishing at the "till" date 00580 timedelta = till - prev_date; 00581 if (prev_value) 00582 { 00583 bucket_available += cur_size * timedelta; 00584 bucket_load += cur_load * timedelta; 00585 bucket_setup += cur_setup * timedelta; 00586 } 00587 else 00588 bucket_unavailable += cur_size * timedelta; 00589 } 00590 else 00591 { 00592 // All time is available on this resource 00593 timedelta = till - prev_date; 00594 bucket_available += cur_size * timedelta; 00595 bucket_load += cur_load * timedelta; 00596 bucket_setup += cur_setup * timedelta; 00597 } 00598 // Remember till which date we already have reported 00599 prev_date = till; 00600 } 00601 00602 00603 PyObject* Resource::PlanIterator::iternext() 00604 { 00605 // Reset counters 00606 bucket_available = 0.0; 00607 bucket_unavailable = 0.0; 00608 bucket_load = 0.0; 00609 bucket_setup = 0.0; 00610 00611 // Get the start and end date of the current bucket 00612 if (start_date) Py_DECREF(start_date); 00613 start_date = end_date; 00614 end_date = PyIter_Next(bucketiterator); 00615 if (!end_date) return NULL; 00616 cur_date = PythonObject(end_date).getDate(); 00617 00618 // Measure from beginning of the bucket till the first event in this bucket 00619 if (ldplaniter != res->getLoadPlans().end() && ldplaniter->getDate() < cur_date) 00620 update(ldplaniter->getDate()); 00621 00622 // Advance the loadplan iterator to the next event date 00623 while (ldplaniter != res->getLoadPlans().end() && ldplaniter->getDate() <= cur_date) 00624 { 00625 // Measure from the previous event till the current one 00626 update(ldplaniter->getDate()); 00627 00628 // Process the event 00629 if (ldplaniter->getType() == 4) 00630 // New max size 00631 cur_size = ldplaniter->getMax(); 00632 else 00633 { 00634 const LoadPlan* ldplan = dynamic_cast<const LoadPlan*>(&*ldplaniter); 00635 assert(ldplan); 00636 if (ldplan->getOperationPlan()->getOperation() == OperationSetup::setupoperation) 00637 // Setup starting or ending 00638 cur_setup = ldplan->getQuantity() < 0 ? 0.0 : cur_size; 00639 else 00640 // Normal load 00641 cur_load = ldplan->getOnhand(); 00642 } 00643 00644 // Move to the next event 00645 ++ldplaniter; 00646 } 00647 00648 // Measure from the previous event till the end of the bucket 00649 update(cur_date); 00650 00651 // Convert from seconds to hours 00652 bucket_available /= 3600; 00653 bucket_load /= 3600; 00654 bucket_unavailable /= 3600; 00655 bucket_setup /= 3600; 00656 00657 // Return the result 00658 return Py_BuildValue("{s:O,s:O,s:d,s:d,s:d,s:d,s:d}", 00659 "start", start_date, 00660 "end", end_date, 00661 "available", bucket_available, 00662 "load", bucket_load, 00663 "unavailable", bucket_unavailable, 00664 "setup", bucket_setup, 00665 "free", bucket_available - bucket_load - bucket_setup); 00666 } 00667 00668 00669 }