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 }

Documentation generated for frePPLe by  doxygen