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