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
