demand.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002   file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/tags/0.8.0/src/model/demand.cpp $
00003   version : $LastChangedRevision: 1108 $  $LastChangedBy: jdetaeye $
00004   date : $LastChangedDate: 2009-12-06 18:54:18 +0100 (Sun, 06 Dec 2009) $
00005  ***************************************************************************/
00006 
00007 /***************************************************************************
00008  *                                                                         *
00009  * Copyright (C) 2007 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 
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, CommandList* 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())
00266     o->writeElement(Tags::tag_minshipment, getMinShipment());
00267 
00268   // Write extra plan information
00269   if ((o->getContentType() == XMLOutput::PLAN
00270       || o->getContentType() == XMLOutput::PLANDETAIL) && !deli.empty())
00271   {
00272     o->BeginObject(Tags::tag_operationplans);
00273     for (OperationPlan_list::const_iterator i=deli.begin(); i!=deli.end(); ++i)
00274       o->writeElement(Tags::tag_operationplan, *i, FULL);
00275     o->EndObject(Tags::tag_operationplans);
00276   }
00277   o->EndObject(tag);
00278 }
00279 
00280 
00281 DECLARE_EXPORT void Demand::beginElement(XMLInput& pIn, const Attribute& pAttr)
00282 {
00283   if (pAttr.isA (Tags::tag_item))
00284     pIn.readto( Item::reader(Item::metadata,pIn.getAttributes()) );
00285   else if (pAttr.isA (Tags::tag_operation))
00286     pIn.readto( Operation::reader(Operation::metadata,pIn.getAttributes()) );
00287   else if (pAttr.isA (Tags::tag_customer))
00288     pIn.readto( Customer::reader(Customer::metadata,pIn.getAttributes()) );
00289   else if (pAttr.isA(Tags::tag_operationplan))
00290     pIn.readto(OperationPlan::createOperationPlan(OperationPlan::metadata,pIn.getAttributes()));
00291   else
00292     HasHierarchy<Demand>::beginElement(pIn, pAttr);
00293 }
00294 
00295 
00296 DECLARE_EXPORT void Demand::endElement(XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement)
00297 {
00298   if (pAttr.isA (Tags::tag_quantity))
00299     setQuantity (pElement.getDouble());
00300   else if (pAttr.isA (Tags::tag_priority))
00301     setPriority (pElement.getInt());
00302   else if (pAttr.isA (Tags::tag_due))
00303     setDue(pElement.getDate());
00304   else if (pAttr.isA (Tags::tag_operation))
00305   {
00306     Operation *o = dynamic_cast<Operation*>(pIn.getPreviousObject());
00307     if (o) setOperation(o);
00308     else throw LogicException("Incorrect object type during read operation");
00309   }
00310   else if (pAttr.isA (Tags::tag_customer))
00311   {
00312     Customer *c = dynamic_cast<Customer*>(pIn.getPreviousObject());
00313     if (c) setCustomer(c);
00314     else throw LogicException("Incorrect object type during read operation");
00315   }
00316   else if (pAttr.isA (Tags::tag_item))
00317   {
00318     Item *i = dynamic_cast<Item*>(pIn.getPreviousObject());
00319     if (i) setItem(i);
00320     else throw LogicException("Incorrect object type during read operation");
00321   }
00322   else if (pAttr.isA (Tags::tag_maxlateness))
00323     setMaxLateness(pElement.getTimeperiod());
00324   else if (pAttr.isA (Tags::tag_minshipment))
00325     setMinShipment(pElement.getDouble());
00326   else if (pAttr.isA(Tags::tag_operationplan))
00327   {
00328     OperationPlan* opplan
00329       = dynamic_cast<OperationPlan*>(pIn.getPreviousObject());
00330     if (opplan) addDelivery(opplan);
00331     else throw LogicException("Incorrect object type during read operation");
00332   }
00333   else
00334   {
00335     Plannable::endElement(pIn, pAttr, pElement);
00336     HasDescription::endElement(pIn, pAttr, pElement);
00337     HasHierarchy<Demand>::endElement (pIn, pAttr, pElement);
00338   }
00339 }
00340 
00341 
00342 DECLARE_EXPORT PyObject* Demand::getattro(const Attribute& attr)
00343 {
00344   if (attr.isA(Tags::tag_name))
00345     return PythonObject(getName());
00346   if (attr.isA(Tags::tag_quantity))
00347     return PythonObject(getQuantity());
00348   if (attr.isA(Tags::tag_due))
00349     return PythonObject(getDue());
00350   if (attr.isA(Tags::tag_priority))
00351     return PythonObject(getPriority());
00352   if (attr.isA(Tags::tag_owner))
00353     return PythonObject(getOwner());
00354   if (attr.isA(Tags::tag_item))
00355     return PythonObject(getItem());
00356   if (attr.isA(Tags::tag_customer))
00357     return PythonObject(getCustomer());
00358   if (attr.isA(Tags::tag_operation))
00359     return PythonObject(getOperation());
00360   if (attr.isA(Tags::tag_description))
00361     return PythonObject(getDescription());
00362   if (attr.isA(Tags::tag_category))
00363     return PythonObject(getCategory());
00364   if (attr.isA(Tags::tag_subcategory))
00365     return PythonObject(getSubCategory());
00366   if (attr.isA(Tags::tag_minshipment))
00367     return PythonObject(getMinShipment());
00368   if (attr.isA(Tags::tag_maxlateness))
00369     return PythonObject(getMaxLateness());
00370   if (attr.isA(Tags::tag_hidden))
00371     return PythonObject(getHidden());
00372   if (attr.isA(Tags::tag_operationplans))
00373     return new DemandPlanIterator(this);
00374   if (attr.isA(Tags::tag_pegging))
00375     return new PeggingIterator(this);
00376   return NULL;
00377 }
00378 
00379 
00380 DECLARE_EXPORT int Demand::setattro(const Attribute& attr, const PythonObject& field)
00381 {
00382   if (attr.isA(Tags::tag_name))
00383     setName(field.getString());
00384   else if (attr.isA(Tags::tag_priority))
00385     setPriority(field.getInt());
00386   else if (attr.isA(Tags::tag_quantity))
00387     setQuantity(field.getDouble());
00388   else if (attr.isA(Tags::tag_due))
00389     setDue(field.getDate());
00390   else if (attr.isA(Tags::tag_item))
00391   {
00392     if (!field.check(Item::metadata))
00393     {
00394       PyErr_SetString(PythonDataException, "demand item must be of type item");
00395       return -1;
00396     }
00397     Item* y = static_cast<Item*>(static_cast<PyObject*>(field));
00398     setItem(y);
00399   }
00400   else if (attr.isA(Tags::tag_customer))
00401   {
00402     if (!field.check(Customer::metadata))
00403     {
00404       PyErr_SetString(PythonDataException, "demand customer must be of type customer");
00405       return -1;
00406     }
00407     Customer* y = static_cast<Customer*>(static_cast<PyObject*>(field));
00408     setCustomer(y);
00409   }
00410   else if (attr.isA(Tags::tag_description))
00411     setDescription(field.getString());
00412   else if (attr.isA(Tags::tag_category))
00413     setCategory(field.getString());
00414   else if (attr.isA(Tags::tag_subcategory))
00415     setSubCategory(field.getString());
00416   else if (attr.isA(Tags::tag_minshipment))
00417     setMinShipment(field.getDouble());
00418   else if (attr.isA(Tags::tag_maxlateness))
00419     setMaxLateness(field.getTimeperiod());
00420   else if (attr.isA(Tags::tag_owner))
00421   {
00422     if (!field.check(Demand::metadata))
00423     {
00424       PyErr_SetString(PythonDataException, "demand owner must be of type demand");
00425       return -1;
00426     }
00427     Demand* y = static_cast<Demand*>(static_cast<PyObject*>(field));
00428     setOwner(y);
00429   }
00430   else if (attr.isA(Tags::tag_operation))
00431   {
00432     if (!field.check(Operation::metadata))
00433     {
00434       PyErr_SetString(PythonDataException, "demand operation must be of type operation");
00435       return -1;
00436     }
00437     Operation* y = static_cast<Operation*>(static_cast<PyObject*>(field));
00438     setOperation(y);
00439   }
00440   else if (attr.isA(Tags::tag_hidden))
00441     setHidden(field.getBool());
00442   else
00443     return -1;  // Error
00444   return 0;  // OK
00445 }
00446 
00447 
00448 int DemandPlanIterator::initialize()
00449 {
00450   // Initialize the type
00451   PythonType& x = PythonExtension<DemandPlanIterator>::getType();
00452   x.setName("demandplanIterator");
00453   x.setDoc("frePPLe iterator for demand delivery operationplans");
00454   x.supportiter();
00455   return x.typeReady();
00456 }
00457 
00458 
00459 PyObject* DemandPlanIterator::iternext()
00460 {
00461   if (i == dem->getDelivery().end()) return NULL;
00462   PyObject* result = const_cast<OperationPlan*>(&**(i++));
00463   Py_INCREF(result);
00464   return result;
00465 }
00466 
00467 } // end namespace