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