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: 1336 $  $LastChangedBy: jdetaeye $
00004   date : $LastChangedDate: 2010-08-16 08:39:23 +0200 (Mon, 16 Aug 2010) $
00005  ***************************************************************************/
00006 
00007 /***************************************************************************
00008  *                                                                         *
00009  * Copyright (C) 2007-2010 by Johan De Taeye                               *
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 
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->instantiate(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, 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   o->writeElement(Tags::tag_minimum, min_cal);
00246   o->writeElement(Tags::tag_maximum, max_cal);
00247   if (getCarryingCost()!= 0.0)
00248     o->writeElement(Tags::tag_carrying_cost, getCarryingCost());
00249 
00250   // Write extra plan information
00251   i = flowplans.begin();
00252   if ((o->getContentType() == XMLOutput::PLAN
00253       || o->getContentType() == XMLOutput::PLANDETAIL) && i!=flowplans.end())
00254   {
00255     o->BeginObject(Tags::tag_flowplans);
00256     for (; i!=flowplans.end(); ++i)
00257       if (i->getType()==1)
00258         dynamic_cast<const FlowPlan*>(&*i)->writeElement(o, Tags::tag_flowplan);
00259     o->EndObject(Tags::tag_flowplans);
00260   }
00261 
00262   // Ending tag
00263   o->EndObject(tag);
00264 }
00265 
00266 
00267 DECLARE_EXPORT void Buffer::beginElement(XMLInput& pIn, const Attribute& pAttr)
00268 {
00269   if (pAttr.isA(Tags::tag_flow)
00270       && pIn.getParentElement().first.isA(Tags::tag_flows))
00271   {
00272     Flow *f =
00273       dynamic_cast<Flow*>(MetaCategory::ControllerDefault(Flow::metadata,pIn.getAttributes()));
00274     if (f) f->setBuffer(this);
00275     pIn.readto (f);
00276   }
00277   else if (pAttr.isA(Tags::tag_producing))
00278     pIn.readto( Operation::reader(Operation::metadata,pIn.getAttributes()) );
00279   else if (pAttr.isA(Tags::tag_item))
00280     pIn.readto( Item::reader(Item::metadata,pIn.getAttributes()) );
00281   else if (pAttr.isA(Tags::tag_minimum) || pAttr.isA(Tags::tag_maximum))
00282     pIn.readto( Calendar::reader(Calendar::metadata,pIn.getAttributes()) );
00283   else if (pAttr.isA(Tags::tag_location))
00284     pIn.readto( Location::reader(Location::metadata,pIn.getAttributes()) );
00285   else if (pAttr.isA(Tags::tag_flowplans))
00286     pIn.IgnoreElement();
00287   else
00288     HasHierarchy<Buffer>::beginElement(pIn, pAttr);
00289 }
00290 
00291 
00292 DECLARE_EXPORT void Buffer::endElement(XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement)
00293 {
00294   if (pAttr.isA(Tags::tag_producing))
00295   {
00296     Operation *b = dynamic_cast<Operation*>(pIn.getPreviousObject());
00297     if (b) setProducingOperation(b);
00298     else throw LogicException("Incorrect object type during read operation");
00299   }
00300   else if (pAttr.isA(Tags::tag_item))
00301   {
00302     Item *a = dynamic_cast<Item*>(pIn.getPreviousObject());
00303     if (a) setItem(a);
00304     else throw LogicException("Incorrect object type during read operation");
00305   }
00306   else if (pAttr.isA(Tags::tag_onhand))
00307     setOnHand(pElement.getDouble());
00308   else if (pAttr.isA(Tags::tag_minimum))
00309   {
00310     CalendarDouble *mincal =
00311       dynamic_cast<CalendarDouble*>(pIn.getPreviousObject());
00312     if (mincal)
00313       setMinimum(mincal);
00314     else
00315     {
00316       Calendar *c = dynamic_cast<Calendar*>(pIn.getPreviousObject());
00317       if (!c)
00318         throw LogicException("Incorrect object type during read operation");
00319       throw DataException("Calendar '" + c->getName() +
00320           "' has invalid type for use as buffer min calendar");
00321     }
00322   }
00323   else if (pAttr.isA(Tags::tag_maximum))
00324   {
00325     CalendarDouble *maxcal =
00326       dynamic_cast<CalendarDouble*>(pIn.getPreviousObject());
00327     if (maxcal)
00328       setMaximum(maxcal);
00329     else
00330     {
00331       Calendar *c = dynamic_cast<Calendar*>(pIn.getPreviousObject());
00332       if (!c)
00333         throw LogicException("Incorrect object type during read operation");
00334       throw DataException("Calendar '" + c->getName() +
00335           "' has invalid type for use as buffer max calendar");
00336     }
00337   }
00338   else if (pAttr.isA(Tags::tag_location))
00339   {
00340     Location * d = dynamic_cast<Location*>(pIn.getPreviousObject());
00341     if (d) setLocation(d);
00342     else throw LogicException("Incorrect object type during read operation");
00343   }
00344   else if (pAttr.isA(Tags::tag_carrying_cost))
00345     setCarryingCost(pElement.getDouble());
00346   else
00347   {
00348     Plannable::endElement(pIn, pAttr, pElement);
00349     HasDescription::endElement(pIn, pAttr, pElement);
00350     HasHierarchy<Buffer>::endElement(pIn, pAttr, pElement);
00351   }
00352 }
00353 
00354 
00355 DECLARE_EXPORT void Buffer::setMinimum(CalendarDouble *cal)
00356 {
00357   // Resetting the same calendar
00358   if (min_cal == cal) return;
00359 
00360   // Mark as changed
00361   setChanged();
00362 
00363   // Calendar is already set: delete previous events.
00364   if (min_cal)
00365   {
00366     for (flowplanlist::iterator oo=flowplans.begin(); oo!=flowplans.end(); )
00367       if (oo->getType() == 3)
00368       {
00369         flowplans.erase(&(*oo));
00370         delete &(*(oo++));
00371       }
00372       else ++oo;
00373   }
00374 
00375   // Null pointer passed
00376   if (!cal) return;
00377 
00378   // Create timeline structures for every event. A new entry is created only
00379   // when the value changes.
00380   min_cal = const_cast< CalendarDouble* >(cal);
00381   double curMin = 0.0;
00382   for (CalendarDouble::EventIterator x(min_cal); x.getDate()<Date::infiniteFuture; ++x)
00383     if (curMin != x.getValue())
00384     {
00385       curMin = x.getValue();
00386       flowplanlist::EventMinQuantity *newBucket =
00387         new flowplanlist::EventMinQuantity(x.getDate(), curMin);
00388       flowplans.insert(newBucket);
00389     }
00390 }
00391 
00392 
00393 DECLARE_EXPORT void Buffer::setMaximum(CalendarDouble *cal)
00394 {
00395   // Resetting the same calendar
00396   if (max_cal == cal) return;
00397 
00398   // Mark as changed
00399   setChanged();
00400 
00401   // Calendar is already set: delete previous events.
00402   if (max_cal)
00403   {
00404     for (flowplanlist::iterator oo=flowplans.begin(); oo!=flowplans.end(); )
00405       if (oo->getType() == 4)
00406       {
00407         flowplans.erase(&(*oo));
00408         delete &(*(oo++));
00409       }
00410       else ++oo;
00411   }
00412 
00413   // Null pointer passed
00414   if (!cal) return;
00415 
00416   // Create timeline structures for every bucket. A new entry is created only
00417   // when the value changes.
00418   max_cal = const_cast<CalendarDouble*>(cal);
00419   double curMax = 0.0;
00420   for (CalendarDouble::EventIterator x(min_cal); x.getDate()<Date::infiniteFuture; ++x)
00421     if (curMax != x.getValue())
00422     {
00423       curMax = x.getValue();
00424       flowplanlist::EventMaxQuantity *newBucket =
00425         new flowplanlist::EventMaxQuantity(x.getDate(), curMax);
00426       flowplans.insert(newBucket);
00427     }
00428 }
00429 
00430 
00431 DECLARE_EXPORT void Buffer::deleteOperationPlans(bool deleteLocked)
00432 {
00433   // Delete the operationplans
00434   for (flowlist::iterator i=flows.begin(); i!=flows.end(); ++i)
00435     OperationPlan::deleteOperationPlans(i->getOperation(),deleteLocked);
00436 
00437   // Mark to recompute the problems
00438   setChanged();
00439 }
00440 
00441 
00442 DECLARE_EXPORT Buffer::~Buffer()
00443 {
00444   // Delete all operationplans.
00445   // An alternative logic would be to delete only the flowplans for this
00446   // buffer and leave the rest of the plan untouched. The currently
00447   // implemented method is way more drastic...
00448   deleteOperationPlans(true);
00449 
00450   // The Flow objects are automatically deleted by the destructor of the
00451   // Association list class.
00452 
00453   // Remove the inventory operation
00454   Operation *invoper = Operation::find(INVENTORY_OPERATION);
00455   if (invoper) delete invoper;
00456 }
00457 
00458 
00459 DECLARE_EXPORT void Buffer::followPegging
00460   (PeggingIterator& iter, FlowPlan* curflowplan, short nextlevel, double curqty, double curfactor)
00461 {
00462 
00463   double peggedQty(0);
00464   Buffer::flowplanlist::const_iterator f = getFlowPlans().begin(curflowplan);
00465 
00466   if (curflowplan->getQuantity() < -ROUNDING_ERROR && !iter.isDownstream())
00467   {
00468     // CASE 1:
00469     // This is a flowplan consuming from a buffer. Navigating upstream means
00470     // finding the flowplans producing this consumed material.
00471     double endQty = f->getCumulativeConsumed();
00472     double startQty = endQty + f->getQuantity();
00473     if (f->getCumulativeProduced() <= startQty)
00474     {
00475       // CASE 1A: Not produced enough yet: move forward
00476       while (f!=getFlowPlans().end()
00477           && f->getCumulativeProduced() <= startQty) ++f;
00478       while (f!=getFlowPlans().end()
00479           && ( (f->getQuantity()<=0 && f->getCumulativeProduced() < endQty)
00480               || (f->getQuantity()>0
00481                   && f->getCumulativeProduced()-f->getQuantity() < endQty))
00482             )
00483       {
00484         if (f->getQuantity() > ROUNDING_ERROR)
00485         {
00486           double newqty = f->getQuantity();
00487           if (f->getCumulativeProduced()-f->getQuantity() < startQty)
00488             newqty -= startQty - (f->getCumulativeProduced()-f->getQuantity());
00489           if (f->getCumulativeProduced() > endQty)
00490             newqty -= f->getCumulativeProduced() - endQty;
00491           peggedQty += newqty;
00492           const FlowPlan *x = dynamic_cast<const FlowPlan*>(&(*f));
00493           iter.updateStack(nextlevel,
00494             -curqty*newqty/curflowplan->getQuantity(),
00495             curfactor*newqty/f->getQuantity(),
00496             curflowplan, x);
00497         }
00498         ++f;
00499       }
00500     }
00501     else
00502     {
00503       // CASE 1B: Produced too much already: move backward
00504       while ( f!=getFlowPlans().end()
00505           && ((f->getQuantity()<=0 && f->getCumulativeProduced() > endQty)
00506               || (f->getQuantity()>0
00507                   && f->getCumulativeProduced()-f->getQuantity() > endQty))) --f;
00508       while (f!=getFlowPlans().end() && f->getCumulativeProduced() > startQty)
00509       {
00510         if (f->getQuantity() > ROUNDING_ERROR)
00511         {
00512           double newqty = f->getQuantity();
00513           if (f->getCumulativeProduced()-f->getQuantity() < startQty)
00514             newqty -= startQty - (f->getCumulativeProduced()-f->getQuantity());
00515           if (f->getCumulativeProduced() > endQty)
00516             newqty -= f->getCumulativeProduced() - endQty;
00517           peggedQty += newqty;
00518           const FlowPlan *x = dynamic_cast<const FlowPlan*>(&(*f));
00519           iter.updateStack(nextlevel,
00520               -curqty*newqty/curflowplan->getQuantity(),
00521               curfactor*newqty/f->getQuantity(),
00522               curflowplan, x);
00523         }
00524         --f;
00525       }
00526     }
00527     if (peggedQty < endQty - startQty - ROUNDING_ERROR)
00528       // Unproduced material (i.e. material that is consumed but never
00529       // produced) is handled with a special entry on the stack.
00530       iter.updateStack(nextlevel,
00531           curqty*(peggedQty - endQty + startQty)/curflowplan->getQuantity(),
00532           curfactor,
00533           curflowplan,
00534           NULL,
00535           false);
00536     return;
00537   }
00538 
00539   if (curflowplan->getQuantity() > ROUNDING_ERROR && iter.isDownstream())
00540   {
00541     // CASE 2:
00542     // This is a flowplan producing in a buffer. Navigating downstream means
00543     // finding the flowplans consuming this produced material.
00544     double endQty = f->getCumulativeProduced();
00545     double startQty = endQty - f->getQuantity();
00546     if (f->getCumulativeConsumed() <= startQty)
00547     {
00548       // CASE 2A: Not consumed enough yet: move forward
00549       while (f!=getFlowPlans().end()
00550           && f->getCumulativeConsumed() <= startQty) ++f;
00551       while (f!=getFlowPlans().end()
00552           && ( (f->getQuantity()<=0
00553               && f->getCumulativeConsumed()+f->getQuantity() < endQty)
00554               || (f->getQuantity()>0 && f->getCumulativeConsumed() < endQty))
00555             )
00556       {
00557         if (f->getQuantity() < -ROUNDING_ERROR)
00558         {
00559           double newqty = - f->getQuantity();
00560           if (f->getCumulativeConsumed()+f->getQuantity() < startQty)
00561             newqty -= startQty - (f->getCumulativeConsumed()+f->getQuantity());
00562           if (f->getCumulativeConsumed() > endQty)
00563             newqty -= f->getCumulativeConsumed() - endQty;
00564           peggedQty += newqty;
00565           const FlowPlan *x = dynamic_cast<const FlowPlan*>(&(*f));
00566           iter.updateStack(nextlevel,
00567             curqty*newqty/curflowplan->getQuantity(),
00568             -curfactor*newqty/f->getQuantity(),
00569             x, curflowplan);
00570         }
00571         ++f;
00572       }
00573     }
00574     else
00575     {
00576       // CASE 2B: Consumed too much already: move backward
00577       while ( f!=getFlowPlans().end()
00578           && ((f->getQuantity()<=0 && f->getCumulativeConsumed()+f->getQuantity() < endQty)
00579               || (f->getQuantity()>0 && f->getCumulativeConsumed() < endQty))) --f;
00580       while (f!=getFlowPlans().end() && f->getCumulativeConsumed() > startQty)
00581       {
00582         if (f->getQuantity() < -ROUNDING_ERROR)
00583         {
00584           double newqty = - f->getQuantity();
00585           if (f->getCumulativeConsumed()+f->getQuantity() < startQty)
00586             newqty -= startQty - (f->getCumulativeConsumed()+f->getQuantity());
00587           if (f->getCumulativeConsumed() > endQty)
00588             newqty -= f->getCumulativeConsumed() - 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             x, curflowplan);
00595         }
00596         --f;
00597       }
00598     }
00599     if (peggedQty < endQty - startQty)
00600       // Unpegged material (i.e. material that is produced but never consumed)
00601       // is handled with a special entry on the stack.
00602       iter.updateStack(nextlevel,
00603         curqty*(endQty - startQty - peggedQty)/curflowplan->getQuantity(),
00604         curfactor,
00605         NULL, curflowplan,
00606         false);
00607     return;
00608   }
00609 }
00610 
00611 
00612 DECLARE_EXPORT void BufferInfinite::writeElement
00613 (XMLOutput *o, const Keyword &tag, mode m) const
00614 {
00615   // Writing a reference
00616   if (m == REFERENCE)
00617   {
00618     o->writeElement
00619       (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type);
00620     return;
00621   }
00622 
00623   // Write the complete object
00624   if (m != NOHEADER) o->BeginObject
00625     (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type);
00626 
00627   // Write the fields and an ending tag
00628   Buffer::writeElement(o, tag, NOHEADER);
00629 }
00630 
00631 
00632 DECLARE_EXPORT void BufferProcure::endElement(XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement)
00633 {
00634   if (pAttr.isA(Tags::tag_leadtime))
00635     setLeadtime(pElement.getTimeperiod());
00636   else if (pAttr.isA(Tags::tag_fence))
00637     setFence(pElement.getTimeperiod());
00638   else if (pAttr.isA(Tags::tag_size_maximum))
00639     setSizeMaximum(pElement.getDouble());
00640   else if (pAttr.isA(Tags::tag_size_minimum))
00641     setSizeMinimum(pElement.getDouble());
00642   else if (pAttr.isA(Tags::tag_size_multiple))
00643     setSizeMultiple(pElement.getDouble());
00644   else if (pAttr.isA(Tags::tag_mininterval))
00645     setMinimumInterval(pElement.getTimeperiod());
00646   else if (pAttr.isA(Tags::tag_maxinterval))
00647     setMaximumInterval(pElement.getTimeperiod());
00648   else if (pAttr.isA(Tags::tag_mininventory))
00649     setMinimumInventory(pElement.getDouble());
00650   else if (pAttr.isA(Tags::tag_maxinventory))
00651     setMaximumInventory(pElement.getDouble());
00652   else
00653     Buffer::endElement(pIn, pAttr, pElement);
00654 }
00655 
00656 
00657 DECLARE_EXPORT void BufferProcure::writeElement(XMLOutput *o, const Keyword &tag, mode m) const
00658 {
00659   // Writing a reference
00660   if (m == REFERENCE)
00661   {
00662     o->writeElement
00663       (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type);
00664     return;
00665   }
00666 
00667   // Write the complete object
00668   if (m != NOHEADER) o->BeginObject
00669     (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type);
00670 
00671   // Write the extra fields
00672   if (leadtime) o->writeElement(Tags::tag_leadtime, leadtime);
00673   if (fence) o->writeElement(Tags::tag_fence, fence);
00674   if (size_maximum != DBL_MAX) o->writeElement(Tags::tag_size_maximum, size_maximum);
00675   if (size_minimum) o->writeElement(Tags::tag_size_minimum, size_minimum);
00676   if (size_multiple) o->writeElement(Tags::tag_size_multiple, size_multiple);
00677   if (min_interval) o->writeElement(Tags::tag_mininterval, min_interval);
00678   if (max_interval) o->writeElement(Tags::tag_maxinterval, max_interval);
00679   if (getMinimumInventory()) o->writeElement(Tags::tag_mininventory, getMinimumInventory());
00680   if (getMaximumInventory()) o->writeElement(Tags::tag_maxinventory, getMaximumInventory());
00681 
00682   // Write the fields and an ending tag
00683   Buffer::writeElement(o, tag, NOHEADER);
00684 }
00685 
00686 
00687 DECLARE_EXPORT Operation* BufferProcure::getOperation() const
00688 {
00689   if (!oper)
00690   {
00691     Operation *o = Operation::find(PROCURE_OPERATION);
00692     if (!o)
00693     {
00694       // Create the operation if it didn't exist yet
00695       o = new OperationFixedTime(PROCURE_OPERATION);
00696       static_cast<OperationFixedTime*>(o)->setDuration(leadtime);
00697       o->setFence(getFence());
00698       // Ideally we would like to hide the procurement operation itself.
00699       // But in that case we need a different way to show the procurements
00700       // to the outside world.
00701       // o->setHidden(true);
00702       Operation::add(o);  // No need to check again for existence
00703       new FlowEnd(o, const_cast<BufferProcure*>(this), 1);
00704     }
00705     const_cast<BufferProcure*>(this)->oper = o;
00706   }
00707   return oper;
00708 }
00709 
00710 
00711 DECLARE_EXPORT PyObject* Buffer::getattro(const Attribute& attr)
00712 {
00713   if (attr.isA(Tags::tag_name))
00714     return PythonObject(getName());
00715   if (attr.isA(Tags::tag_description))
00716     return PythonObject(getDescription());
00717   if (attr.isA(Tags::tag_category))
00718     return PythonObject(getCategory());
00719   if (attr.isA(Tags::tag_subcategory))
00720     return PythonObject(getSubCategory());
00721   if (attr.isA(Tags::tag_owner))
00722     return PythonObject(getOwner());
00723   if (attr.isA(Tags::tag_location))
00724     return PythonObject(getLocation());
00725   if (attr.isA(Tags::tag_producing))
00726     return PythonObject(getProducingOperation());
00727   if (attr.isA(Tags::tag_item))
00728     return PythonObject(getItem());
00729   if (attr.isA(Tags::tag_onhand))
00730     return PythonObject(getOnHand());
00731   if (attr.isA(Tags::tag_flowplans))
00732     return new FlowPlanIterator(this);
00733   if (attr.isA(Tags::tag_maximum))
00734     return PythonObject(getMaximum());
00735   if (attr.isA(Tags::tag_minimum))
00736     return PythonObject(getMinimum());
00737   if (attr.isA(Tags::tag_carrying_cost))
00738     return PythonObject(getCarryingCost());
00739   if (attr.isA(Tags::tag_hidden))
00740     return PythonObject(getHidden());
00741   if (attr.isA(Tags::tag_flows))
00742     return new FlowIterator(this);
00743   if (attr.isA(Tags::tag_level))
00744     return PythonObject(getLevel());
00745   if (attr.isA(Tags::tag_cluster))
00746     return PythonObject(getCluster());
00747   // @todo support member iteration for buffer, res, dem, item, ...
00748   // PythonBufferIterator becomes an abstract class: defines the pytype and an abstract iternext.
00749   // 2 subclasses then implement it: an iterator over all buffers, and another one over all members.
00750   return NULL;
00751 }
00752 
00753 
00754 DECLARE_EXPORT int Buffer::setattro(const Attribute& attr, const PythonObject& field)
00755 {
00756   if (attr.isA(Tags::tag_name))
00757     setName(field.getString());
00758   else if (attr.isA(Tags::tag_description))
00759     setDescription(field.getString());
00760   else if (attr.isA(Tags::tag_category))
00761     setCategory(field.getString());
00762   else if (attr.isA(Tags::tag_subcategory))
00763     setSubCategory(field.getString());
00764   else if (attr.isA(Tags::tag_owner))
00765   {
00766     if (!field.check(Buffer::metadata))
00767     {
00768       PyErr_SetString(PythonDataException, "buffer owner must be of type buffer");
00769       return -1;
00770     }
00771     Buffer* y = static_cast<Buffer*>(static_cast<PyObject*>(field));
00772     setOwner(y);
00773   }
00774   else if (attr.isA(Tags::tag_location))
00775   {
00776     if (!field.check(Location::metadata))
00777     {
00778       PyErr_SetString(PythonDataException, "buffer location must be of type location");
00779       return -1;
00780     }
00781     Location* y = static_cast<Location*>(static_cast<PyObject*>(field));
00782     setLocation(y);
00783   }
00784   else if (attr.isA(Tags::tag_item))
00785   {
00786     if (!field.check(Item::metadata))
00787     {
00788       PyErr_SetString(PythonDataException, "buffer item must be of type item");
00789       return -1;
00790     }
00791     Item* y = static_cast<Item*>(static_cast<PyObject*>(field));
00792     setItem(y);
00793   }
00794   else if (attr.isA(Tags::tag_maximum))
00795   {
00796     if (!field.check(CalendarDouble::metadata))
00797     {
00798       PyErr_SetString(PythonDataException, "buffer maximum must be of type calendar_double");
00799       return -1;
00800     }
00801     CalendarDouble* y = static_cast<CalendarDouble*>(static_cast<PyObject*>(field));
00802     setMaximum(y);
00803   }
00804   else if (attr.isA(Tags::tag_minimum))
00805   {
00806     if (!field.check(CalendarDouble::metadata))
00807     {
00808       PyErr_SetString(PythonDataException, "buffer minimum must be of type calendar_double");
00809       return -1;
00810     }
00811     CalendarDouble* y = static_cast<CalendarDouble*>(static_cast<PyObject*>(field));
00812     setMinimum(y);
00813   }
00814   else if (attr.isA(Tags::tag_onhand))
00815     setOnHand(field.getDouble());
00816   else if (attr.isA(Tags::tag_carrying_cost))
00817     setCarryingCost(field.getDouble());
00818   else if (attr.isA(Tags::tag_producing))
00819   {
00820     if (!field.check(Operation::metadata))
00821     {
00822       PyErr_SetString(PythonDataException, "buffer producing must be of type operation");
00823       return -1;
00824     }
00825     Operation* y = static_cast<Operation*>(static_cast<PyObject*>(field));
00826     setProducingOperation(y);
00827   }
00828   else if (attr.isA(Tags::tag_hidden))
00829     setHidden(field.getBool());
00830   else
00831     return -1;  // Error
00832   return 0;  // OK
00833 }
00834 
00835 
00836 DECLARE_EXPORT PyObject* BufferProcure::getattro(const Attribute& attr)
00837 {
00838   if (attr.isA(Tags::tag_leadtime))
00839     return PythonObject(getLeadtime());
00840   if (attr.isA(Tags::tag_mininventory))
00841     return PythonObject(getMinimumInventory());
00842   if (attr.isA(Tags::tag_maxinventory))
00843     return PythonObject(getMaximumInventory());
00844   if (attr.isA(Tags::tag_mininterval))
00845     return PythonObject(getMinimumInterval());
00846   if (attr.isA(Tags::tag_maxinterval))
00847     return PythonObject(getMaximumInterval());
00848   if (attr.isA(Tags::tag_fence))
00849     return PythonObject(getFence());
00850   if (attr.isA(Tags::tag_size_minimum))
00851     return PythonObject(getSizeMinimum());
00852   if (attr.isA(Tags::tag_size_multiple))
00853     return PythonObject(getSizeMultiple());
00854   if (attr.isA(Tags::tag_size_maximum))
00855     return PythonObject(getSizeMaximum());
00856   return Buffer::getattro(attr);
00857 }
00858 
00859 
00860 DECLARE_EXPORT int BufferProcure::setattro(const Attribute& attr, const PythonObject& field)
00861 {
00862   if (attr.isA(Tags::tag_leadtime))
00863     setLeadtime(field.getTimeperiod());
00864   else if (attr.isA(Tags::tag_mininventory))
00865     setMinimumInventory(field.getDouble());
00866   else if (attr.isA(Tags::tag_maxinventory))
00867     setMaximumInventory(field.getDouble());
00868   else if (attr.isA(Tags::tag_mininterval))
00869     setMinimumInterval(field.getTimeperiod());
00870   else if (attr.isA(Tags::tag_maxinterval))
00871     setMaximumInterval(field.getTimeperiod());
00872   else if (attr.isA(Tags::tag_size_minimum))
00873     setSizeMinimum(field.getDouble());
00874   else if (attr.isA(Tags::tag_size_multiple))
00875     setSizeMultiple(field.getDouble());
00876   else if (attr.isA(Tags::tag_size_maximum))
00877     setSizeMaximum(field.getDouble());
00878   else if (attr.isA(Tags::tag_fence))
00879     setFence(field.getTimeperiod());
00880   else
00881     return Buffer::setattro(attr, field);
00882   return 0;
00883 }
00884 
00885 } // end namespace

Documentation generated for frePPLe by  doxygen