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

Documentation generated for frePPLe by  doxygen