buffer.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002   file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/trunk/src/model/buffer.cpp $
00003   version : $LastChangedRevision: 1505 $  $LastChangedBy: jdetaeye $
00004   date : $LastChangedDate: 2011-08-26 18:55:08 +0200 (Fri, 26 Aug 2011) $
00005  ***************************************************************************/
00006 
00007 /***************************************************************************
00008  *                                                                         *
00009  * Copyright (C) 2007-2011 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 Lesser General Public License as published   *
00013  * by the Free Software Foundation; either version 2.1 of the License, or  *
00014  * (at your option) any later version.                                     *
00015  *                                                                         *
00016  * This library is distributed in the hope that it will be useful,         *
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of          *
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser *
00019  * General Public License for more details.                                *
00020  *                                                                         *
00021  * You should have received a copy of the GNU Lesser General Public        *
00022  * License along with this library; if not, write to the Free Software     *
00023  * Foundation Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 *
00024  * USA                                                                     *
00025  *                                                                         *
00026  ***************************************************************************/
00027 
00028 #define FREPPLE_CORE
00029 #include "frepple/model.h"
00030 #include <math.h>
00031 
00032 // This is the name used for the dummy operation used to represent the
00033 // inventory.
00034 #define INVENTORY_OPERATION "Inventory of buffer '" + string(getName()) + "'"
00035 
00036 // This is the name used for the dummy operation used to represent procurements
00037 #define PROCURE_OPERATION "Procure for buffer '" + string(getName()) + "'"
00038 
00039 namespace frepple
00040 {
00041 
00042 template<class Buffer> DECLARE_EXPORT Tree utils::HasName<Buffer>::st;
00043 DECLARE_EXPORT const MetaCategory* Buffer::metadata;
00044 DECLARE_EXPORT const MetaClass* BufferDefault::metadata,
00045   *BufferInfinite::metadata,
00046   *BufferProcure::metadata;
00047 DECLARE_EXPORT const double Buffer::default_max = 1e37;
00048 
00049 
00050 int Buffer::initialize()
00051 {
00052   // Initialize the metadata
00053   metadata = new MetaCategory("buffer", "buffers", reader, writer);
00054 
00055   // Initialize the Python class
00056   return FreppleCategory<Buffer>::initialize();
00057 }
00058 
00059 
00060 int BufferDefault::initialize()
00061 {
00062   // Initialize the metadata
00063   BufferDefault::metadata = new MetaClass(
00064     "buffer",
00065     "buffer_default",
00066     Object::createString<BufferDefault>, true);
00067 
00068   // Initialize the Python class
00069   return FreppleClass<BufferDefault,Buffer>::initialize();
00070 }
00071 
00072 
00073 int BufferInfinite::initialize()
00074 {
00075   // Initialize the metadata
00076   BufferInfinite::metadata = new MetaClass(
00077     "buffer",
00078     "buffer_infinite",
00079     Object::createString<BufferInfinite>);
00080 
00081   // Initialize the Python class
00082   return FreppleClass<BufferInfinite,Buffer>::initialize();
00083 }
00084 
00085 
00086 int BufferProcure::initialize()
00087 {
00088   // Initialize the metadata
00089   BufferProcure::metadata = new MetaClass(
00090     "buffer",
00091     "buffer_procure",
00092     Object::createString<BufferProcure>);
00093 
00094   // Initialize the Python class
00095   return FreppleClass<BufferProcure,Buffer>::initialize();
00096 }
00097 
00098 
00099 DECLARE_EXPORT void Buffer::setOnHand(double f)
00100 {
00101   // The dummy operation to model the inventory may need to be created
00102   Operation *o = Operation::find(INVENTORY_OPERATION);
00103   Flow *fl;
00104   if (!o)
00105   {
00106     // Create a fixed time operation with zero leadtime, hidden from the xml
00107     // output, hidden for the solver, and without problem detection.
00108     o = new OperationFixedTime(INVENTORY_OPERATION);
00109     Operation::add(o);  // No need to check again for existance
00110     o->setHidden(true);
00111     o->setDetectProblems(false);
00112     fl = new FlowEnd(o, this, 1);
00113   }
00114   else
00115     // Find the flow of this operation
00116     fl = const_cast<Flow*>(&*(o->getFlows().begin()));
00117 
00118   // Check valid pointers
00119   if (!fl || !o)
00120     throw LogicException("Failed creating inventory operation for '"
00121         + getName() + "'");
00122 
00123   // Make sure the sign of the flow is correct: +1 or -1.
00124   fl->setQuantity(f>=0.0 ? 1.0 : -1.0);
00125 
00126   // Create a dummy operationplan on the inventory operation
00127   OperationPlan::iterator i(o);
00128   if (i == OperationPlan::end())
00129   {
00130     // No operationplan exists yet
00131     OperationPlan *opplan = o->createOperationPlan(
00132       fabs(f), Date::infinitePast, Date::infinitePast);
00133     opplan->setLocked(true);
00134     // Note that we use the max counter for the onhand operationplans.
00135     opplan->activate(false);
00136   }
00137   else
00138   {
00139     // Update the existing operationplan
00140     i->setLocked(false);
00141     i->setQuantity(fabs(f));
00142     i->setLocked(true);
00143   }
00144   setChanged();
00145 }
00146 
00147 
00148 DECLARE_EXPORT double Buffer::getOnHand(Date d) const
00149 {
00150   double tmp(0.0);
00151   for (flowplanlist::const_iterator oo=flowplans.begin();
00152       oo!=flowplans.end(); ++oo)
00153   {
00154     if (oo->getDate() > d)
00155       // Found a flowplan with a later date.
00156       // Return the onhand after the previous flowplan.
00157       return tmp;
00158     tmp = oo->getOnhand();
00159   }
00160   // Found no flowplan: either we have specified a date later than the
00161   // last flowplan, either there are no flowplans at all.
00162   return tmp;
00163 }
00164 
00165 
00166 DECLARE_EXPORT double Buffer::getOnHand(Date d1, Date d2, bool min) const
00167 {
00168   // Swap parameters if required
00169   if (d2 < d1)
00170   {
00171     Date x(d1);
00172     d2 = d1;
00173     d2 = x;
00174   }
00175 
00176   // Loop through all flowplans
00177   double tmp(0.0), record(0.0);
00178   Date d, prev_Date;
00179   for (flowplanlist::const_iterator oo=flowplans.begin(); true; ++oo)
00180   {
00181     if (oo==flowplans.end() || oo->getDate() > d)
00182     {
00183       // Date has now changed or we have arrived at the end
00184 
00185       // New max?
00186       if (prev_Date < d1)
00187         // Not in active Date range: we simply follow the onhand profile
00188         record = tmp;
00189       else
00190       {
00191         // In the active range
00192         // New extreme?
00193         if (min) {if (tmp < record) record = tmp;}
00194         else {if (tmp > record) record = tmp;}
00195       }
00196 
00197       // Are we done now?
00198       if (prev_Date > d2 || oo==flowplans.end()) return record;
00199 
00200       // Set the variable with the new Date
00201       d = oo->getDate();
00202     }
00203     tmp = oo->getOnhand();
00204     prev_Date = oo->getDate();
00205   }
00206   // The above for-loop controls the exit. This line of code is never reached.
00207   throw LogicException("Unreachable code reached");
00208 }
00209 
00210 
00211 DECLARE_EXPORT void Buffer::writeElement(XMLOutput *o, const Keyword &tag, mode m) const
00212 {
00213   // Writing a reference
00214   if (m == REFERENCE)
00215   {
00216     o->writeElement(tag, Tags::tag_name, getName());
00217     return;
00218   }
00219 
00220   // Write the complete object
00221   if (m!= NOHEADER) o->BeginObject(tag, Tags::tag_name, getName());
00222 
00223   // Write own fields
00224   HasDescription::writeElement(o, tag);
00225   HasHierarchy<Buffer>::writeElement(o, tag);
00226   o->writeElement(Tags::tag_producing, producing_operation);
00227   o->writeElement(Tags::tag_item, it);
00228   o->writeElement(Tags::tag_location, loc);
00229   Plannable::writeElement(o, tag);
00230 
00231   // Onhand
00232   flowplanlist::const_iterator i = flowplans.begin();
00233   // Loop through the flowplans at the start of the horizon
00234   for (; i!=flowplans.end() && i->getType()!=1 && !i->getDate(); ++i) ;
00235   if (i!=flowplans.end() && i->getType()==1)
00236   {
00237     // A flowplan has been found
00238     const FlowPlan *fp = dynamic_cast<const FlowPlan*>(&*i);
00239     if (fp
00240         && fp->getFlow()->getOperation()->getName() == string(INVENTORY_OPERATION)
00241         && fabs(fp->getQuantity()) > ROUNDING_ERROR)
00242       o->writeElement(Tags::tag_onhand, fp->getQuantity());
00243   }
00244 
00245   // Minimum and maximum inventory targets, carrying cost
00246   if (min_val != 0) o->writeElement(Tags::tag_minimum, min_val);
00247   o->writeElement(Tags::tag_minimum_calendar, min_cal);
00248   if (max_val != default_max) o->writeElement(Tags::tag_maximum, max_val);
00249   o->writeElement(Tags::tag_maximum_calendar, max_cal);
00250   if (getCarryingCost()!= 0.0)
00251     o->writeElement(Tags::tag_carrying_cost, getCarryingCost());
00252 
00253   // Write extra plan information
00254   i = flowplans.begin();
00255   if ((o->getContentType() == XMLOutput::PLAN
00256       || o->getContentType() == XMLOutput::PLANDETAIL) && i!=flowplans.end())
00257   {
00258     o->BeginObject(Tags::tag_flowplans);
00259     for (; i!=flowplans.end(); ++i)
00260       if (i->getType()==1)
00261         dynamic_cast<const FlowPlan*>(&*i)->writeElement(o, Tags::tag_flowplan);
00262     o->EndObject(Tags::tag_flowplans);
00263   }
00264 
00265   // Ending tag
00266   o->EndObject(tag);
00267 }
00268 
00269 
00270 DECLARE_EXPORT void Buffer::beginElement(XMLInput& pIn, const Attribute& pAttr)
00271 {
00272   if (pAttr.isA(Tags::tag_flow)
00273       && pIn.getParentElement().first.isA(Tags::tag_flows))
00274   {
00275     Flow *f =
00276       dynamic_cast<Flow*>(MetaCategory::ControllerDefault(Flow::metadata,pIn.getAttributes()));
00277     if (f) f->setBuffer(this);
00278     pIn.readto (f);
00279   }
00280   else if (pAttr.isA(Tags::tag_producing))
00281     pIn.readto( Operation::reader(Operation::metadata,pIn.getAttributes()) );
00282   else if (pAttr.isA(Tags::tag_item))
00283     pIn.readto( Item::reader(Item::metadata,pIn.getAttributes()) );
00284   else if (pAttr.isA(Tags::tag_minimum_calendar)
00285     || pAttr.isA(Tags::tag_maximum_calendar))
00286     pIn.readto( Calendar::reader(Calendar::metadata,pIn.getAttributes()) );
00287   else if (pAttr.isA(Tags::tag_location))
00288     pIn.readto( Location::reader(Location::metadata,pIn.getAttributes()) );
00289   else if (pAttr.isA(Tags::tag_flowplans))
00290     pIn.IgnoreElement();
00291   else
00292     HasHierarchy<Buffer>::beginElement(pIn, pAttr);
00293 }
00294 
00295 
00296 DECLARE_EXPORT void Buffer::endElement(XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement)
00297 {
00298   if (pAttr.isA(Tags::tag_producing))
00299   {
00300     Operation *b = dynamic_cast<Operation*>(pIn.getPreviousObject());
00301     if (b) setProducingOperation(b);
00302     else throw LogicException("Incorrect object type during read operation");
00303   }
00304   else if (pAttr.isA(Tags::tag_item))
00305   {
00306     Item *a = dynamic_cast<Item*>(pIn.getPreviousObject());
00307     if (a) setItem(a);
00308     else throw LogicException("Incorrect object type during read operation");
00309   }
00310   else if (pAttr.isA(Tags::tag_onhand))
00311     setOnHand(pElement.getDouble());
00312   else if (pAttr.isA(Tags::tag_minimum))
00313     setMinimum(pElement.getDouble());
00314   else if (pAttr.isA(Tags::tag_maximum))
00315     setMaximum(pElement.getDouble());
00316   else if (pAttr.isA(Tags::tag_minimum_calendar))
00317   {
00318     CalendarDouble *mincal =
00319       dynamic_cast<CalendarDouble*>(pIn.getPreviousObject());
00320     if (mincal)
00321       setMinimumCalendar(mincal);
00322     else
00323     {
00324       Calendar *c = dynamic_cast<Calendar*>(pIn.getPreviousObject());
00325       if (!c)
00326         throw LogicException("Incorrect object type during read operation");
00327       throw DataException("Calendar '" + c->getName() +
00328           "' has invalid type for use as buffer min calendar");
00329     }
00330   }
00331   else if (pAttr.isA(Tags::tag_maximum_calendar))
00332   {
00333     CalendarDouble *maxcal =
00334       dynamic_cast<CalendarDouble*>(pIn.getPreviousObject());
00335     if (maxcal)
00336       setMaximumCalendar(maxcal);
00337     else
00338     {
00339       Calendar *c = dynamic_cast<Calendar*>(pIn.getPreviousObject());
00340       if (!c)
00341         throw LogicException("Incorrect object type during read operation");
00342       throw DataException("Calendar '" + c->getName() +
00343           "' has invalid type for use as buffer max calendar");
00344     }
00345   }
00346   else if (pAttr.isA(Tags::tag_location))
00347   {
00348     Location * d = dynamic_cast<Location*>(pIn.getPreviousObject());
00349     if (d) setLocation(d);
00350     else throw LogicException("Incorrect object type during read operation");
00351   }
00352   else if (pAttr.isA(Tags::tag_carrying_cost))
00353     setCarryingCost(pElement.getDouble());
00354   else
00355   {
00356     Plannable::endElement(pIn, pAttr, pElement);
00357     HasDescription::endElement(pIn, pAttr, pElement);
00358     HasHierarchy<Buffer>::endElement(pIn, pAttr, pElement);
00359   }
00360 }
00361 
00362 
00363 DECLARE_EXPORT void Buffer::setMinimum(double m)
00364 {
00365   // There is already a minimum calendar.
00366   if (min_cal)
00367   {
00368     // We update the field, but don't use it yet.
00369     min_val = m;
00370     return;
00371   }
00372 
00373   // Mark as changed
00374   setChanged();
00375 
00376   // Set field
00377   min_val = m;
00378 
00379   // Create or update a single timeline min event
00380   for (flowplanlist::iterator oo=flowplans.begin(); oo!=flowplans.end(); oo++)
00381     if (oo->getType() == 3)
00382     {
00383       // Update existing event
00384       static_cast<flowplanlist::EventMinQuantity *>(&*oo)->setMin(min_val);
00385       return;
00386     }
00387   // Create new event
00388   flowplanlist::EventMinQuantity *newEvent =
00389     new flowplanlist::EventMinQuantity(Date::infinitePast, min_val);
00390   flowplans.insert(newEvent);
00391 }
00392 
00393 
00394 DECLARE_EXPORT void Buffer::setMinimumCalendar(CalendarDouble *cal)
00395 {
00396   // Resetting the same calendar
00397   if (min_cal == cal) return;
00398 
00399   // Mark as changed
00400   setChanged();
00401 
00402   // Delete previous events.
00403   for (flowplanlist::iterator oo=flowplans.begin(); oo!=flowplans.end(); )
00404     if (oo->getType() == 3)
00405     {
00406       flowplans.erase(&(*oo));
00407       delete &(*(oo++));
00408     }
00409     else ++oo;
00410 
00411   // Null pointer passed. Change back to time independent min.
00412   if (!cal)
00413   {
00414     setMinimum(min_val);
00415     return;
00416   }
00417 
00418   // Create timeline structures for every event. A new entry is created only
00419   // when the value changes.
00420   min_cal = const_cast< CalendarDouble* >(cal);
00421   double curMin = 0.0;
00422   for (CalendarDouble::EventIterator x(min_cal); x.getDate()<Date::infiniteFuture; ++x)
00423     if (curMin != x.getValue())
00424     {
00425       curMin = x.getValue();
00426       flowplanlist::EventMinQuantity *newBucket =
00427         new flowplanlist::EventMinQuantity(x.getDate(), curMin);
00428       flowplans.insert(newBucket);
00429     }
00430 }
00431 
00432 
00433 DECLARE_EXPORT void Buffer::setMaximum(double m)
00434 {
00435   // There is already a maximum calendar.
00436   if (max_cal)
00437   {
00438     // We update the field, but don't use it yet.
00439     max_val = m;
00440     return;
00441   }
00442 
00443   // Mark as changed
00444   setChanged();
00445 
00446   // Set field
00447   max_val = m;
00448 
00449   // Create or update a single timeline max event
00450   for (flowplanlist::iterator oo=flowplans.begin(); oo!=flowplans.end(); oo++)
00451     if (oo->getType() == 4)
00452     {
00453       // Update existing event
00454       static_cast<flowplanlist::EventMaxQuantity *>(&*oo)->setMax(max_val);
00455       return;
00456     }
00457   // Create new event
00458   flowplanlist::EventMaxQuantity *newEvent =
00459     new flowplanlist::EventMaxQuantity(Date::infinitePast, max_val);
00460   flowplans.insert(newEvent);
00461 }
00462 
00463 
00464 DECLARE_EXPORT void Buffer::setMaximumCalendar(CalendarDouble *cal)
00465 {
00466   // Resetting the same calendar
00467   if (max_cal == cal) return;
00468 
00469   // Mark as changed
00470   setChanged();
00471 
00472   // Delete previous events.
00473   for (flowplanlist::iterator oo=flowplans.begin(); oo!=flowplans.end(); )
00474     if (oo->getType() == 4)
00475     {
00476       flowplans.erase(&(*oo));
00477       delete &(*(oo++));
00478     }
00479     else ++oo;
00480 
00481   // Null pointer passed. Change back to time independent max.
00482   if (!cal)
00483   {
00484     setMaximum(max_val);
00485     return;
00486   }
00487 
00488   // Create timeline structures for every bucket. A new entry is created only
00489   // when the value changes.
00490   max_cal = const_cast<CalendarDouble*>(cal);
00491   double curMax = 0.0;
00492   for (CalendarDouble::EventIterator x(max_cal); x.getDate()<Date::infiniteFuture; ++x)
00493     if (curMax != x.getValue())
00494     {
00495       curMax = x.getValue();
00496       flowplanlist::EventMaxQuantity *newBucket =
00497         new flowplanlist::EventMaxQuantity(x.getDate(), curMax);
00498       flowplans.insert(newBucket);
00499     }
00500 }
00501 
00502 
00503 DECLARE_EXPORT void Buffer::deleteOperationPlans(bool deleteLocked)
00504 {
00505   // Delete the operationplans
00506   for (flowlist::iterator i=flows.begin(); i!=flows.end(); ++i)
00507     OperationPlan::deleteOperationPlans(i->getOperation(),deleteLocked);
00508 
00509   // Mark to recompute the problems
00510   setChanged();
00511 }
00512 
00513 
00514 DECLARE_EXPORT Buffer::~Buffer()
00515 {
00516   // Delete all operationplans.
00517   // An alternative logic would be to delete only the flowplans for this
00518   // buffer and leave the rest of the plan untouched. The currently
00519   // implemented method is way more drastic...
00520   deleteOperationPlans(true);
00521 
00522   // The Flow objects are automatically deleted by the destructor of the
00523   // Association list class.
00524 
00525   // Remove the inventory operation
00526   Operation *invoper = Operation::find(INVENTORY_OPERATION);
00527   if (invoper) delete invoper;
00528 }
00529 
00530 
00531 DECLARE_EXPORT void Buffer::followPegging
00532   (PeggingIterator& iter, FlowPlan* curflowplan, short nextlevel, double curqty, double curfactor)
00533 {
00534 
00535   double peggedQty(0);
00536   Buffer::flowplanlist::const_iterator f = getFlowPlans().begin(curflowplan);
00537 
00538   if (curflowplan->getQuantity() < -ROUNDING_ERROR && !iter.isDownstream())
00539   {
00540     // CASE 1:
00541     // This is a flowplan consuming from a buffer. Navigating upstream means
00542     // finding the flowplans producing this consumed material.
00543     double endQty = f->getCumulativeConsumed();
00544     double startQty = endQty + f->getQuantity();
00545     if (f->getCumulativeProduced() <= startQty)
00546     {
00547       // CASE 1A: Not produced enough yet: move forward
00548       while (f!=getFlowPlans().end()
00549           && f->getCumulativeProduced() <= startQty) ++f;
00550       while (f!=getFlowPlans().end()
00551           && ( (f->getQuantity()<=0 && f->getCumulativeProduced() < endQty)
00552               || (f->getQuantity()>0
00553                   && f->getCumulativeProduced()-f->getQuantity() < endQty))
00554             )
00555       {
00556         if (f->getQuantity() > ROUNDING_ERROR)
00557         {
00558           double newqty = f->getQuantity();
00559           if (f->getCumulativeProduced()-f->getQuantity() < startQty)
00560             newqty -= startQty - (f->getCumulativeProduced()-f->getQuantity());
00561           if (f->getCumulativeProduced() > endQty)
00562             newqty -= f->getCumulativeProduced() - endQty;
00563           peggedQty += newqty;
00564           const FlowPlan *x = dynamic_cast<const FlowPlan*>(&(*f));
00565           iter.updateStack(nextlevel,
00566             -curqty*newqty/curflowplan->getQuantity(),
00567             curfactor*newqty/f->getQuantity(),
00568             curflowplan, x);
00569         }
00570         ++f;
00571       }
00572     }
00573     else
00574     {
00575       // CASE 1B: Produced too much already: move backward
00576       while ( f!=getFlowPlans().end()
00577           && ((f->getQuantity()<=0 && f->getCumulativeProduced() > endQty)
00578               || (f->getQuantity()>0
00579                   && f->getCumulativeProduced()-f->getQuantity() > endQty))) --f;
00580       while (f!=getFlowPlans().end() && f->getCumulativeProduced() > startQty)
00581       {
00582         if (f->getQuantity() > ROUNDING_ERROR)
00583         {
00584           double newqty = f->getQuantity();
00585           if (f->getCumulativeProduced()-f->getQuantity() < startQty)
00586             newqty -= startQty - (f->getCumulativeProduced()-f->getQuantity());
00587           if (f->getCumulativeProduced() > endQty)
00588             newqty -= f->getCumulativeProduced() - endQty;
00589           peggedQty += newqty;
00590           const FlowPlan *x = dynamic_cast<const FlowPlan*>(&(*f));
00591           iter.updateStack(nextlevel,
00592               -curqty*newqty/curflowplan->getQuantity(),
00593               curfactor*newqty/f->getQuantity(),
00594               curflowplan, x);
00595         }
00596         --f;
00597       }
00598     }
00599     if (peggedQty < endQty - startQty - ROUNDING_ERROR)
00600       // Unproduced material (i.e. material that is consumed but never
00601       // produced) is handled with a special entry on the stack.
00602       iter.updateStack(nextlevel,
00603           curqty*(peggedQty - endQty + startQty)/curflowplan->getQuantity(),
00604           curfactor,
00605           curflowplan,
00606           NULL,
00607           false);
00608     return;
00609   }
00610 
00611   if (curflowplan->getQuantity() > ROUNDING_ERROR && iter.isDownstream())
00612   {
00613     // CASE 2:
00614     // This is a flowplan producing in a buffer. Navigating downstream means
00615     // finding the flowplans consuming this produced material.
00616     double endQty = f->getCumulativeProduced();
00617     double startQty = endQty - f->getQuantity();
00618     if (f->getCumulativeConsumed() <= startQty)
00619     {
00620       // CASE 2A: Not consumed enough yet: move forward
00621       while (f!=getFlowPlans().end()
00622           && f->getCumulativeConsumed() <= startQty) ++f;
00623       while (f!=getFlowPlans().end()
00624           && ( (f->getQuantity()<=0
00625               && f->getCumulativeConsumed()+f->getQuantity() < endQty)
00626               || (f->getQuantity()>0 && f->getCumulativeConsumed() < endQty))
00627             )
00628       {
00629         if (f->getQuantity() < -ROUNDING_ERROR)
00630         {
00631           double newqty = - f->getQuantity();
00632           if (f->getCumulativeConsumed()+f->getQuantity() < startQty)
00633             newqty -= startQty - (f->getCumulativeConsumed()+f->getQuantity());
00634           if (f->getCumulativeConsumed() > endQty)
00635             newqty -= f->getCumulativeConsumed() - endQty;
00636           peggedQty += newqty;
00637           const FlowPlan *x = dynamic_cast<const FlowPlan*>(&(*f));
00638           iter.updateStack(nextlevel,
00639             curqty*newqty/curflowplan->getQuantity(),
00640             -curfactor*newqty/f->getQuantity(),
00641             x, curflowplan);
00642         }
00643         ++f;
00644       }
00645     }
00646     else
00647     {
00648       // CASE 2B: Consumed too much already: move backward
00649       while ( f!=getFlowPlans().end()
00650           && ((f->getQuantity()<=0 && f->getCumulativeConsumed()+f->getQuantity() < endQty)
00651               || (f->getQuantity()>0 && f->getCumulativeConsumed() < endQty))) --f;
00652       while (f!=getFlowPlans().end() && f->getCumulativeConsumed() > startQty)
00653       {
00654         if (f->getQuantity() < -ROUNDING_ERROR)
00655         {
00656           double newqty = - f->getQuantity();
00657           if (f->getCumulativeConsumed()+f->getQuantity() < startQty)
00658             newqty -= startQty - (f->getCumulativeConsumed()+f->getQuantity());
00659           if (f->getCumulativeConsumed() > endQty)
00660             newqty -= f->getCumulativeConsumed() - endQty;
00661           peggedQty += newqty;
00662           const FlowPlan *x = dynamic_cast<const FlowPlan*>(&(*f));
00663           iter.updateStack(nextlevel,
00664             curqty*newqty/curflowplan->getQuantity(),
00665             -curfactor*newqty/f->getQuantity(),
00666             x, curflowplan);
00667         }
00668         --f;
00669       }
00670     }
00671     if (peggedQty < endQty - startQty)
00672       // Unpegged material (i.e. material that is produced but never consumed)
00673       // is handled with a special entry on the stack.
00674       iter.updateStack(nextlevel,
00675         curqty*(endQty - startQty - peggedQty)/curflowplan->getQuantity(),
00676         curfactor,
00677         NULL, curflowplan,
00678         false);
00679     return;
00680   }
00681 }
00682 
00683 
00684 DECLARE_EXPORT void BufferInfinite::writeElement
00685 (XMLOutput *o, const Keyword &tag, mode m) const
00686 {
00687   // Writing a reference
00688   if (m == REFERENCE)
00689   {
00690     o->writeElement
00691       (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type);
00692     return;
00693   }
00694 
00695   // Write the complete object
00696   if (m != NOHEADER) o->BeginObject
00697     (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type);
00698 
00699   // Write the fields and an ending tag
00700   Buffer::writeElement(o, tag, NOHEADER);
00701 }
00702 
00703 
00704 DECLARE_EXPORT void BufferProcure::endElement(XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement)
00705 {
00706   if (pAttr.isA(Tags::tag_leadtime))
00707     setLeadtime(pElement.getTimeperiod());
00708   else if (pAttr.isA(Tags::tag_fence))
00709     setFence(pElement.getTimeperiod());
00710   else if (pAttr.isA(Tags::tag_size_maximum))
00711     setSizeMaximum(pElement.getDouble());
00712   else if (pAttr.isA(Tags::tag_size_minimum))
00713     setSizeMinimum(pElement.getDouble());
00714   else if (pAttr.isA(Tags::tag_size_multiple))
00715     setSizeMultiple(pElement.getDouble());
00716   else if (pAttr.isA(Tags::tag_mininterval))
00717     setMinimumInterval(pElement.getTimeperiod());
00718   else if (pAttr.isA(Tags::tag_maxinterval))
00719     setMaximumInterval(pElement.getTimeperiod());
00720   else if (pAttr.isA(Tags::tag_mininventory))
00721     setMinimumInventory(pElement.getDouble());
00722   else if (pAttr.isA(Tags::tag_maxinventory))
00723     setMaximumInventory(pElement.getDouble());
00724   else
00725     Buffer::endElement(pIn, pAttr, pElement);
00726 }
00727 
00728 
00729 DECLARE_EXPORT void BufferProcure::writeElement(XMLOutput *o, const Keyword &tag, mode m) const
00730 {
00731   // Writing a reference
00732   if (m == REFERENCE)
00733   {
00734     o->writeElement
00735       (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type);
00736     return;
00737   }
00738 
00739   // Write the complete object
00740   if (m != NOHEADER) o->BeginObject
00741     (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type);
00742 
00743   // Write the extra fields
00744   if (leadtime) o->writeElement(Tags::tag_leadtime, leadtime);
00745   if (fence) o->writeElement(Tags::tag_fence, fence);
00746   if (size_maximum != DBL_MAX) o->writeElement(Tags::tag_size_maximum, size_maximum);
00747   if (size_minimum) o->writeElement(Tags::tag_size_minimum, size_minimum);
00748   if (size_multiple) o->writeElement(Tags::tag_size_multiple, size_multiple);
00749   if (min_interval) o->writeElement(Tags::tag_mininterval, min_interval);
00750   if (max_interval) o->writeElement(Tags::tag_maxinterval, max_interval);
00751   if (getMinimumInventory()) o->writeElement(Tags::tag_mininventory, getMinimumInventory());
00752   if (getMaximumInventory()) o->writeElement(Tags::tag_maxinventory, getMaximumInventory());
00753 
00754   // Write the fields and an ending tag
00755   Buffer::writeElement(o, tag, NOHEADER);
00756 }
00757 
00758 
00759 DECLARE_EXPORT Operation* BufferProcure::getOperation() const
00760 {
00761   if (!oper)
00762   {
00763     Operation *o = Operation::find(PROCURE_OPERATION);
00764     if (!o)
00765     {
00766       // Create the operation if it didn't exist yet
00767       o = new OperationFixedTime(PROCURE_OPERATION);
00768       static_cast<OperationFixedTime*>(o)->setDuration(leadtime);
00769       o->setFence(getFence());
00770       // Ideally we would like to hide the procurement operation itself.
00771       // But in that case we need a different way to show the procurements
00772       // to the outside world.
00773       // o->setHidden(true);
00774       Operation::add(o);  // No need to check again for existence
00775       new FlowEnd(o, const_cast<BufferProcure*>(this), 1);
00776     }
00777     const_cast<BufferProcure*>(this)->oper = o;
00778   }
00779   return oper;
00780 }
00781 
00782 
00783 DECLARE_EXPORT PyObject* Buffer::getattro(const Attribute& attr)
00784 {
00785   if (attr.isA(Tags::tag_name))
00786     return PythonObject(getName());
00787   if (attr.isA(Tags::tag_description))
00788     return PythonObject(getDescription());
00789   if (attr.isA(Tags::tag_category))
00790     return PythonObject(getCategory());
00791   if (attr.isA(Tags::tag_subcategory))
00792     return PythonObject(getSubCategory());
00793   if (attr.isA(Tags::tag_owner))
00794     return PythonObject(getOwner());
00795   if (attr.isA(Tags::tag_location))
00796     return PythonObject(getLocation());
00797   if (attr.isA(Tags::tag_producing))
00798     return PythonObject(getProducingOperation());
00799   if (attr.isA(Tags::tag_item))
00800     return PythonObject(getItem());
00801   if (attr.isA(Tags::tag_onhand))
00802     return PythonObject(getOnHand());
00803   if (attr.isA(Tags::tag_flowplans))
00804     return new FlowPlanIterator(this);
00805   if (attr.isA(Tags::tag_maximum))
00806     return PythonObject(getMaximum());
00807   if (attr.isA(Tags::tag_minimum))
00808     return PythonObject(getMinimum());
00809   if (attr.isA(Tags::tag_maximum_calendar))
00810     return PythonObject(getMaximumCalendar());
00811   if (attr.isA(Tags::tag_minimum_calendar))
00812     return PythonObject(getMinimumCalendar());
00813   if (attr.isA(Tags::tag_carrying_cost))
00814     return PythonObject(getCarryingCost());
00815   if (attr.isA(Tags::tag_hidden))
00816     return PythonObject(getHidden());
00817   if (attr.isA(Tags::tag_flows))
00818     return new FlowIterator(this);
00819   if (attr.isA(Tags::tag_level))
00820     return PythonObject(getLevel());
00821   if (attr.isA(Tags::tag_cluster))
00822     return PythonObject(getCluster());
00823   if (attr.isA(Tags::tag_members))
00824   return new BufferIterator(this);
00825   return NULL;
00826 }
00827 
00828 
00829 DECLARE_EXPORT int Buffer::setattro(const Attribute& attr, const PythonObject& field)
00830 {
00831   if (attr.isA(Tags::tag_name))
00832     setName(field.getString());
00833   else if (attr.isA(Tags::tag_description))
00834     setDescription(field.getString());
00835   else if (attr.isA(Tags::tag_category))
00836     setCategory(field.getString());
00837   else if (attr.isA(Tags::tag_subcategory))
00838     setSubCategory(field.getString());
00839   else if (attr.isA(Tags::tag_owner))
00840   {
00841     if (!field.check(Buffer::metadata))
00842     {
00843       PyErr_SetString(PythonDataException, "buffer owner must be of type buffer");
00844       return -1;
00845     }
00846     Buffer* y = static_cast<Buffer*>(static_cast<PyObject*>(field));
00847     setOwner(y);
00848   }
00849   else if (attr.isA(Tags::tag_location))
00850   {
00851     if (!field.check(Location::metadata))
00852     {
00853       PyErr_SetString(PythonDataException, "buffer location must be of type location");
00854       return -1;
00855     }
00856     Location* y = static_cast<Location*>(static_cast<PyObject*>(field));
00857     setLocation(y);
00858   }
00859   else if (attr.isA(Tags::tag_item))
00860   {
00861     if (!field.check(Item::metadata))
00862     {
00863       PyErr_SetString(PythonDataException, "buffer item must be of type item");
00864       return -1;
00865     }
00866     Item* y = static_cast<Item*>(static_cast<PyObject*>(field));
00867     setItem(y);
00868   }
00869   else if (attr.isA(Tags::tag_minimum))
00870     setMinimum(field.getDouble());
00871   else if (attr.isA(Tags::tag_maximum))
00872     setMaximum(field.getDouble());
00873   else if (attr.isA(Tags::tag_maximum_calendar))
00874   {
00875     if (!field.check(CalendarDouble::metadata))
00876     {
00877       PyErr_SetString(PythonDataException, "buffer maximum must be of type calendar_double");
00878       return -1;
00879     }
00880     CalendarDouble* y = static_cast<CalendarDouble*>(static_cast<PyObject*>(field));
00881     setMaximumCalendar(y);
00882   }
00883   else if (attr.isA(Tags::tag_minimum_calendar))
00884   {
00885     if (!field.check(CalendarDouble::metadata))
00886     {
00887       PyErr_SetString(PythonDataException, "buffer minimum must be of type calendar_double");
00888       return -1;
00889     }
00890     CalendarDouble* y = static_cast<CalendarDouble*>(static_cast<PyObject*>(field));
00891     setMinimumCalendar(y);
00892   }
00893   else if (attr.isA(Tags::tag_onhand))
00894     setOnHand(field.getDouble());
00895   else if (attr.isA(Tags::tag_carrying_cost))
00896     setCarryingCost(field.getDouble());
00897   else if (attr.isA(Tags::tag_producing))
00898   {
00899     if (!field.check(Operation::metadata))
00900     {
00901       PyErr_SetString(PythonDataException, "buffer producing must be of type operation");
00902       return -1;
00903     }
00904     Operation* y = static_cast<Operation*>(static_cast<PyObject*>(field));
00905     setProducingOperation(y);
00906   }
00907   else if (attr.isA(Tags::tag_hidden))
00908     setHidden(field.getBool());
00909   else
00910     return -1;  // Error
00911   return 0;  // OK
00912 }
00913 
00914 
00915 DECLARE_EXPORT PyObject* BufferProcure::getattro(const Attribute& attr)
00916 {
00917   if (attr.isA(Tags::tag_leadtime))
00918     return PythonObject(getLeadtime());
00919   if (attr.isA(Tags::tag_mininventory))
00920     return PythonObject(getMinimumInventory());
00921   if (attr.isA(Tags::tag_maxinventory))
00922     return PythonObject(getMaximumInventory());
00923   if (attr.isA(Tags::tag_mininterval))
00924     return PythonObject(getMinimumInterval());
00925   if (attr.isA(Tags::tag_maxinterval))
00926     return PythonObject(getMaximumInterval());
00927   if (attr.isA(Tags::tag_fence))
00928     return PythonObject(getFence());
00929   if (attr.isA(Tags::tag_size_minimum))
00930     return PythonObject(getSizeMinimum());
00931   if (attr.isA(Tags::tag_size_multiple))
00932     return PythonObject(getSizeMultiple());
00933   if (attr.isA(Tags::tag_size_maximum))
00934     return PythonObject(getSizeMaximum());
00935   return Buffer::getattro(attr);
00936 }
00937 
00938 
00939 DECLARE_EXPORT int BufferProcure::setattro(const Attribute& attr, const PythonObject& field)
00940 {
00941   if (attr.isA(Tags::tag_leadtime))
00942     setLeadtime(field.getTimeperiod());
00943   else if (attr.isA(Tags::tag_mininventory))
00944     setMinimumInventory(field.getDouble());
00945   else if (attr.isA(Tags::tag_maxinventory))
00946     setMaximumInventory(field.getDouble());
00947   else if (attr.isA(Tags::tag_mininterval))
00948     setMinimumInterval(field.getTimeperiod());
00949   else if (attr.isA(Tags::tag_maxinterval))
00950     setMaximumInterval(field.getTimeperiod());
00951   else if (attr.isA(Tags::tag_size_minimum))
00952     setSizeMinimum(field.getDouble());
00953   else if (attr.isA(Tags::tag_size_multiple))
00954     setSizeMultiple(field.getDouble());
00955   else if (attr.isA(Tags::tag_size_maximum))
00956     setSizeMaximum(field.getDouble());
00957   else if (attr.isA(Tags::tag_fence))
00958     setFence(field.getTimeperiod());
00959   else
00960     return Buffer::setattro(attr, field);
00961   return 0;
00962 }
00963 
00964 } // end namespace

Documentation generated for frePPLe by  doxygen