buffer.cpp
Go to the documentation of this file.
00001 /*************************************************************************** 00002 file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/tags/0.9.1/src/model/buffer.cpp $ 00003 version : $LastChangedRevision: 1656 $ $LastChangedBy: jdetaeye $ 00004 date : $LastChangedDate: 2012-03-27 19:05:34 +0200 (Tue, 27 Mar 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 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