demand.cpp
Go to the documentation of this file.
00001 /*************************************************************************** 00002 file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/trunk/src/model/demand.cpp $ 00003 version : $LastChangedRevision: 1505 $ $LastChangedBy: jdetaeye $ 00004 date : $LastChangedDate: 2011-08-26 18:55:08 +0200 (Fri, 26 Aug 2011) $ 00005 ***************************************************************************/ 00006 00007 /*************************************************************************** 00008 * * 00009 * Copyright (C) 2007-2011 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 00031 namespace frepple 00032 { 00033 00034 template<class Demand> DECLARE_EXPORT Tree utils::HasName<Demand>::st; 00035 DECLARE_EXPORT const MetaCategory* Demand::metadata; 00036 DECLARE_EXPORT const MetaClass* DemandDefault::metadata; 00037 00038 00039 int Demand::initialize() 00040 { 00041 // Initialize the metadata 00042 metadata = new MetaCategory("demand", "demands", reader, writer); 00043 00044 // Initialize the Python class 00045 return FreppleCategory<Demand>::initialize(); 00046 } 00047 00048 00049 int DemandDefault::initialize() 00050 { 00051 // Initialize the metadata 00052 DemandDefault::metadata = new MetaClass( 00053 "demand", 00054 "demand_default", 00055 Object::createString<DemandDefault>, true); 00056 00057 // Initialize the Python class 00058 return FreppleClass<DemandDefault,Demand>::initialize(); 00059 } 00060 00061 00062 DECLARE_EXPORT void Demand::setQuantity(double f) 00063 { 00064 // Reject negative quantities, and no-change updates 00065 double delta(f - qty); 00066 if (f < 0.0 || fabs(delta)<ROUNDING_ERROR) return; 00067 00068 // Update the quantity 00069 qty = f; 00070 setChanged(); 00071 } 00072 00073 00074 DECLARE_EXPORT void Demand::deleteOperationPlans 00075 (bool deleteLocked, CommandManager* cmds) 00076 { 00077 // Delete all opplans 00078 // Note that an extra loop is used to assure that our iterator doesn't get 00079 // invalidated during the deletion. 00080 while (true) 00081 { 00082 // Find a candidate to delete 00083 OperationPlan *candidate = NULL; 00084 for (OperationPlan_list::iterator i = deli.begin(); i!=deli.end(); ++i) 00085 if (deleteLocked || !(*i)->getLocked()) 00086 { 00087 candidate = *i; 00088 break; 00089 } 00090 if (!candidate) break; 00091 if (cmds) 00092 // Use delete command 00093 cmds->add(new CommandDeleteOperationPlan(candidate)); 00094 else 00095 // Delete immediately 00096 delete candidate; 00097 } 00098 00099 // Mark the demand as being changed, so the problems can be redetected 00100 setChanged(); 00101 } 00102 00103 00104 DECLARE_EXPORT void Demand::removeDelivery(OperationPlan * o) 00105 { 00106 // Valid opplan check 00107 if (!o) return; 00108 00109 // See if the demand field on the operationplan points to this demand 00110 if (o->dmd != this) 00111 throw LogicException("Delivery operationplan incorrectly registered"); 00112 00113 // Remove the reference on the operationplan 00114 o->dmd = NULL; // Required to avoid endless loop 00115 o->setDemand(NULL); 00116 00117 // Find in the list of deliveries 00118 OperationPlan_list::iterator j = deli.begin(); 00119 while (j!=deli.end() && *j!=o) ++j; 00120 00121 // Check that the operation is found 00122 // It is possible it is not found! This happens if e.g. an operationplan 00123 // is created but destroyed again before it is initialized. 00124 if (j!=deli.end()) 00125 { 00126 // Remove from the list 00127 deli.erase(j); 00128 // Mark the demand as being changed, so the problems can be redetected 00129 setChanged(); 00130 } 00131 } 00132 00133 00134 DECLARE_EXPORT const Demand::OperationPlan_list& Demand::getDelivery() const 00135 { 00136 // We need to check the sorting order of the list first! It could be disturbed 00137 // when operationplans are being moved around. 00138 // The sorting routine isn't very efficient, but should suffice since the 00139 // list of delivery operationplans is short and isn't expected to be 00140 // disturbed very often. 00141 for (bool swapped(!deli.empty()); swapped; swapped=false) 00142 { 00143 OperationPlan_list::iterator j = const_cast<Demand*>(this)->deli.begin(); 00144 ++j; 00145 for (OperationPlan_list::iterator i = 00146 const_cast<Demand*>(this)->deli.begin(); 00147 j!=const_cast<Demand*>(this)->deli.end(); ++j) 00148 { 00149 if ((*i)->getDates().getEnd() < (*j)->getDates().getEnd()) 00150 { 00151 // Oh yes, the ordering was disrupted indeed... 00152 iter_swap(i,j); 00153 // The Borland compiler doesn't understand that this variable is used. 00154 // It gives a incorrect warning message... 00155 swapped = true; 00156 break; 00157 } 00158 ++i; 00159 } 00160 } 00161 00162 return deli; 00163 } 00164 00165 00166 DECLARE_EXPORT OperationPlan* Demand::getLatestDelivery() const 00167 { 00168 const Demand::OperationPlan_list& l = getDelivery(); 00169 return l.empty() ? NULL : *(l.begin()); 00170 } 00171 00172 00173 DECLARE_EXPORT OperationPlan* Demand::getEarliestDelivery() const 00174 { 00175 const Demand::OperationPlan_list& l = getDelivery(); 00176 OperationPlan *last = NULL; 00177 for (Demand::OperationPlan_list::const_iterator i = l.begin(); i!=l.end(); ++i) 00178 last = *i; 00179 return last; 00180 } 00181 00182 00183 DECLARE_EXPORT void Demand::addDelivery (OperationPlan * o) 00184 { 00185 // Dummy call to this function 00186 if (!o) return; 00187 00188 // Check if it is already in the list. 00189 // If it is, simply exit the function. No need to give a warning message 00190 // since it's harmless. 00191 for (OperationPlan_list::iterator i = deli.begin(); i!=deli.end(); ++i) 00192 if (*i == o) return; 00193 00194 // Add to the list of delivery operationplans. The insertion is such 00195 // that the delivery list is sorted in terms of descending end time. 00196 // i.e. the opplan with the latest end date is on the front of the list. 00197 // Note: We're forcing resorting the deliveries with the getDelivery() 00198 // method. Operation plans dates could have changed, thus disturbing the 00199 // original order. 00200 getDelivery(); 00201 OperationPlan_list::iterator j = deli.begin(); 00202 while (j!=deli.end() && (*j)->getDates().getEnd()>o->getDates().getEnd()) ++j; 00203 deli.insert(j, o); 00204 00205 // Mark the demand as being changed, so the problems can be redetected 00206 setChanged(); 00207 00208 // Create link between operationplan and demand 00209 o->setDemand(this); 00210 00211 // Check validity of operation being used 00212 Operation* tmpOper = getDeliveryOperation(); 00213 if (tmpOper && tmpOper != o->getOperation()) 00214 logger << "Warning: Delivery Operation '" << o->getOperation() 00215 << "' different than expected '" << tmpOper 00216 << "' for demand '" << this << "'" << endl; 00217 } 00218 00219 00220 DECLARE_EXPORT Operation* Demand::getDeliveryOperation() const 00221 { 00222 // Operation can be specified on the demand itself, 00223 if (oper) return oper; 00224 // ... or on the item, 00225 if (it) return it->getOperation(); 00226 // ... or it doesn't exist at all 00227 return NULL; 00228 } 00229 00230 00231 DECLARE_EXPORT double Demand::getPlannedQuantity() const 00232 { 00233 double delivered(0.0); 00234 for (OperationPlan_list::const_iterator i=deli.begin(); i!=deli.end(); ++i) 00235 delivered += (*i)->getQuantity(); 00236 return delivered; 00237 } 00238 00239 00240 DECLARE_EXPORT void Demand::writeElement(XMLOutput *o, const Keyword& tag, mode m) const 00241 { 00242 // Writing a reference 00243 if (m == REFERENCE) 00244 { 00245 o->writeElement(tag, Tags::tag_name, getName()); 00246 return; 00247 } 00248 00249 // Write the complete object 00250 if (m != NOHEADER) o->BeginObject(tag, Tags::tag_name, getName()); 00251 00252 // Write the fields 00253 HasDescription::writeElement(o, tag); 00254 HasHierarchy<Demand>::writeElement(o, tag); 00255 o->writeElement(Tags::tag_operation, oper); 00256 o->writeElement(Tags::tag_customer, cust); 00257 Plannable::writeElement(o, tag); 00258 00259 o->writeElement(Tags::tag_quantity, qty); 00260 o->writeElement(Tags::tag_item, it); 00261 o->writeElement(Tags::tag_due, dueDate); 00262 if (getPriority()) o->writeElement(Tags::tag_priority, getPriority()); 00263 if (getMaxLateness() != TimePeriod::MAX) 00264 o->writeElement(Tags::tag_maxlateness, getMaxLateness()); 00265 if (getMinShipment() != 1.0) 00266 o->writeElement(Tags::tag_minshipment, getMinShipment()); 00267 00268 // Write extra plan information 00269 if (o->getContentType() == XMLOutput::PLAN 00270 || o->getContentType() == XMLOutput::PLANDETAIL) 00271 { 00272 if (!deli.empty()) 00273 { 00274 o->BeginObject(Tags::tag_operationplans); 00275 for (OperationPlan_list::const_iterator i=deli.begin(); i!=deli.end(); ++i) 00276 o->writeElement(Tags::tag_operationplan, *i, FULL); 00277 o->EndObject(Tags::tag_operationplans); 00278 } 00279 if (!constraints.empty()) 00280 { 00281 o->BeginObject(Tags::tag_constraints); 00282 for (Problem::const_iterator i = constraints.begin(); i != constraints.end(); ++i) 00283 o->writeElement(Tags::tag_problem, *i, FULL); 00284 o->EndObject(Tags::tag_constraints); 00285 } 00286 } 00287 o->EndObject(tag); 00288 } 00289 00290 00291 DECLARE_EXPORT void Demand::beginElement(XMLInput& pIn, const Attribute& pAttr) 00292 { 00293 if (pAttr.isA (Tags::tag_item)) 00294 pIn.readto( Item::reader(Item::metadata,pIn.getAttributes()) ); 00295 else if (pAttr.isA (Tags::tag_operation)) 00296 pIn.readto( Operation::reader(Operation::metadata,pIn.getAttributes()) ); 00297 else if (pAttr.isA (Tags::tag_customer)) 00298 pIn.readto( Customer::reader(Customer::metadata,pIn.getAttributes()) ); 00299 else if (pAttr.isA(Tags::tag_operationplan)) 00300 pIn.readto(OperationPlan::createOperationPlan(OperationPlan::metadata,pIn.getAttributes())); 00301 else 00302 HasHierarchy<Demand>::beginElement(pIn, pAttr); 00303 } 00304 00305 00306 DECLARE_EXPORT void Demand::endElement(XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement) 00307 { 00308 if (pAttr.isA (Tags::tag_quantity)) 00309 setQuantity (pElement.getDouble()); 00310 else if (pAttr.isA (Tags::tag_priority)) 00311 setPriority (pElement.getInt()); 00312 else if (pAttr.isA (Tags::tag_due)) 00313 setDue(pElement.getDate()); 00314 else if (pAttr.isA (Tags::tag_operation)) 00315 { 00316 Operation *o = dynamic_cast<Operation*>(pIn.getPreviousObject()); 00317 if (o) setOperation(o); 00318 else throw LogicException("Incorrect object type during read operation"); 00319 } 00320 else if (pAttr.isA (Tags::tag_customer)) 00321 { 00322 Customer *c = dynamic_cast<Customer*>(pIn.getPreviousObject()); 00323 if (c) setCustomer(c); 00324 else throw LogicException("Incorrect object type during read operation"); 00325 } 00326 else if (pAttr.isA (Tags::tag_item)) 00327 { 00328 Item *i = dynamic_cast<Item*>(pIn.getPreviousObject()); 00329 if (i) setItem(i); 00330 else throw LogicException("Incorrect object type during read operation"); 00331 } 00332 else if (pAttr.isA (Tags::tag_maxlateness)) 00333 setMaxLateness(pElement.getTimeperiod()); 00334 else if (pAttr.isA (Tags::tag_minshipment)) 00335 setMinShipment(pElement.getDouble()); 00336 else if (pAttr.isA(Tags::tag_operationplan)) 00337 { 00338 OperationPlan* opplan 00339 = dynamic_cast<OperationPlan*>(pIn.getPreviousObject()); 00340 if (opplan) addDelivery(opplan); 00341 else throw LogicException("Incorrect object type during read operation"); 00342 } 00343 else 00344 { 00345 Plannable::endElement(pIn, pAttr, pElement); 00346 HasDescription::endElement(pIn, pAttr, pElement); 00347 HasHierarchy<Demand>::endElement (pIn, pAttr, pElement); 00348 } 00349 } 00350 00351 00352 DECLARE_EXPORT PyObject* Demand::getattro(const Attribute& attr) 00353 { 00354 if (attr.isA(Tags::tag_name)) 00355 return PythonObject(getName()); 00356 if (attr.isA(Tags::tag_quantity)) 00357 return PythonObject(getQuantity()); 00358 if (attr.isA(Tags::tag_due)) 00359 return PythonObject(getDue()); 00360 if (attr.isA(Tags::tag_priority)) 00361 return PythonObject(getPriority()); 00362 if (attr.isA(Tags::tag_owner)) 00363 return PythonObject(getOwner()); 00364 if (attr.isA(Tags::tag_item)) 00365 return PythonObject(getItem()); 00366 if (attr.isA(Tags::tag_customer)) 00367 return PythonObject(getCustomer()); 00368 if (attr.isA(Tags::tag_operation)) 00369 return PythonObject(getOperation()); 00370 if (attr.isA(Tags::tag_description)) 00371 return PythonObject(getDescription()); 00372 if (attr.isA(Tags::tag_category)) 00373 return PythonObject(getCategory()); 00374 if (attr.isA(Tags::tag_subcategory)) 00375 return PythonObject(getSubCategory()); 00376 if (attr.isA(Tags::tag_minshipment)) 00377 return PythonObject(getMinShipment()); 00378 if (attr.isA(Tags::tag_maxlateness)) 00379 return PythonObject(getMaxLateness()); 00380 if (attr.isA(Tags::tag_hidden)) 00381 return PythonObject(getHidden()); 00382 if (attr.isA(Tags::tag_operationplans)) 00383 return new DemandPlanIterator(this); 00384 if (attr.isA(Tags::tag_pegging)) 00385 return new PeggingIterator(this); 00386 if (attr.isA(Tags::tag_constraints)) 00387 return new ProblemIterator(*(constraints.begin())); 00388 if (attr.isA(Tags::tag_members)) 00389 return new DemandIterator(this); 00390 return NULL; 00391 } 00392 00393 00394 DECLARE_EXPORT int Demand::setattro(const Attribute& attr, const PythonObject& field) 00395 { 00396 if (attr.isA(Tags::tag_name)) 00397 setName(field.getString()); 00398 else if (attr.isA(Tags::tag_priority)) 00399 setPriority(field.getInt()); 00400 else if (attr.isA(Tags::tag_quantity)) 00401 setQuantity(field.getDouble()); 00402 else if (attr.isA(Tags::tag_due)) 00403 setDue(field.getDate()); 00404 else if (attr.isA(Tags::tag_item)) 00405 { 00406 if (!field.check(Item::metadata)) 00407 { 00408 PyErr_SetString(PythonDataException, "demand item must be of type item"); 00409 return -1; 00410 } 00411 Item* y = static_cast<Item*>(static_cast<PyObject*>(field)); 00412 setItem(y); 00413 } 00414 else if (attr.isA(Tags::tag_customer)) 00415 { 00416 if (!field.check(Customer::metadata)) 00417 { 00418 PyErr_SetString(PythonDataException, "demand customer must be of type customer"); 00419 return -1; 00420 } 00421 Customer* y = static_cast<Customer*>(static_cast<PyObject*>(field)); 00422 setCustomer(y); 00423 } 00424 else if (attr.isA(Tags::tag_description)) 00425 setDescription(field.getString()); 00426 else if (attr.isA(Tags::tag_category)) 00427 setCategory(field.getString()); 00428 else if (attr.isA(Tags::tag_subcategory)) 00429 setSubCategory(field.getString()); 00430 else if (attr.isA(Tags::tag_minshipment)) 00431 setMinShipment(field.getDouble()); 00432 else if (attr.isA(Tags::tag_maxlateness)) 00433 setMaxLateness(field.getTimeperiod()); 00434 else if (attr.isA(Tags::tag_owner)) 00435 { 00436 if (!field.check(Demand::metadata)) 00437 { 00438 PyErr_SetString(PythonDataException, "demand owner must be of type demand"); 00439 return -1; 00440 } 00441 Demand* y = static_cast<Demand*>(static_cast<PyObject*>(field)); 00442 setOwner(y); 00443 } 00444 else if (attr.isA(Tags::tag_operation)) 00445 { 00446 if (!field.check(Operation::metadata)) 00447 { 00448 PyErr_SetString(PythonDataException, "demand operation must be of type operation"); 00449 return -1; 00450 } 00451 Operation* y = static_cast<Operation*>(static_cast<PyObject*>(field)); 00452 setOperation(y); 00453 } 00454 else if (attr.isA(Tags::tag_hidden)) 00455 setHidden(field.getBool()); 00456 else 00457 return -1; // Error 00458 return 0; // OK 00459 } 00460 00461 00462 int DemandPlanIterator::initialize() 00463 { 00464 // Initialize the type 00465 PythonType& x = PythonExtension<DemandPlanIterator>::getType(); 00466 x.setName("demandplanIterator"); 00467 x.setDoc("frePPLe iterator for demand delivery operationplans"); 00468 x.supportiter(); 00469 return x.typeReady(); 00470 } 00471 00472 00473 PyObject* DemandPlanIterator::iternext() 00474 { 00475 if (i == dem->getDelivery().end()) return NULL; 00476 PyObject* result = const_cast<OperationPlan*>(&**(i++)); 00477 Py_INCREF(result); 00478 return result; 00479 } 00480 00481 } // end namespace