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

Documentation generated for frePPLe by  doxygen