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

Documentation generated for frePPLe by  doxygen