demand.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * *
3  * Copyright (C) 2007-2012 by Johan De Taeye, frePPLe bvba *
4  * *
5  * This library is free software; you can redistribute it and/or modify it *
6  * under the terms of the GNU Affero General Public License as published *
7  * by the Free Software Foundation; either version 3 of the License, or *
8  * (at your option) any later version. *
9  * *
10  * This library is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU Affero General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU Affero General Public *
16  * License along with this program. *
17  * If not, see <http://www.gnu.org/licenses/>. *
18  * *
19  ***************************************************************************/
20 
21 #define FREPPLE_CORE
22 #include "frepple/model.h"
23 
24 namespace frepple
25 {
26 
27 template<class Demand> DECLARE_EXPORT Tree utils::HasName<Demand>::st;
30 
31 
33 {
34  // Initialize the metadata
35  metadata = new MetaCategory("demand", "demands", reader, writer);
36 
37  // Initialize the Python class
39 }
40 
41 
43 {
44  // Initialize the metadata
46  "demand",
47  "demand_default",
48  Object::createString<DemandDefault>, true);
49 
50  // Initialize the Python class
52 }
53 
54 
56 {
57  // Reject negative quantities, and no-change updates
58  double delta(f - qty);
59  if (f < 0.0 || fabs(delta)<ROUNDING_ERROR) return;
60 
61  // Update the quantity
62  qty = f;
63  setChanged();
64 }
65 
66 
68 (bool deleteLocked, CommandManager* cmds)
69 {
70  // Delete all opplans
71  // Note that an extra loop is used to assure that our iterator doesn't get
72  // invalidated during the deletion.
73  while (true)
74  {
75  // Find a candidate to delete
76  OperationPlan *candidate = NULL;
77  for (OperationPlan_list::iterator i = deli.begin(); i!=deli.end(); ++i)
78  if (deleteLocked || !(*i)->getLocked())
79  {
80  candidate = *i;
81  break;
82  }
83  if (!candidate) break;
84  if (cmds)
85  // Use delete command
86  cmds->add(new CommandDeleteOperationPlan(candidate));
87  else
88  // Delete immediately
89  delete candidate;
90  }
91 
92  // Mark the demand as being changed, so the problems can be redetected
93  setChanged();
94 }
95 
96 
98 {
99  // Valid opplan check
100  if (!o) return;
101 
102  // See if the demand field on the operationplan points to this demand
103  if (o->dmd != this)
104  throw LogicException("Delivery operationplan incorrectly registered");
105 
106  // Remove the reference on the operationplan
107  o->dmd = NULL; // Required to avoid endless loop
108  o->setDemand(NULL);
109 
110  // Find in the list of deliveries
111  OperationPlan_list::iterator j = deli.begin();
112  while (j!=deli.end() && *j!=o) ++j;
113 
114  // Check that the operation is found
115  // It is possible it is not found! This happens if e.g. an operationplan
116  // is created but destroyed again before it is initialized.
117  if (j!=deli.end())
118  {
119  // Remove from the list
120  deli.erase(j);
121  // Mark the demand as being changed, so the problems can be redetected
122  setChanged();
123  }
124 }
125 
126 
128 {
129  // We need to check the sorting order of the list first! It could be disturbed
130  // when operationplans are being moved around.
131  // The sorting routine isn't very efficient, but should suffice since the
132  // list of delivery operationplans is short and isn't expected to be
133  // disturbed very often.
134  for (bool swapped(!deli.empty()); swapped; swapped=false)
135  {
136  OperationPlan_list::iterator j = const_cast<Demand*>(this)->deli.begin();
137  ++j;
138  for (OperationPlan_list::iterator i =
139  const_cast<Demand*>(this)->deli.begin();
140  j!=const_cast<Demand*>(this)->deli.end(); ++j)
141  {
142  if ((*i)->getDates().getEnd() < (*j)->getDates().getEnd())
143  {
144  // Oh yes, the ordering was disrupted indeed...
145  iter_swap(i,j);
146  // The Borland compiler doesn't understand that this variable is used.
147  // It gives a incorrect warning message...
148  swapped = true;
149  break;
150  }
151  ++i;
152  }
153  }
154 
155  return deli;
156 }
157 
158 
160 {
162  return l.empty() ? NULL : *(l.begin());
163 }
164 
165 
167 {
169  OperationPlan *last = NULL;
170  for (Demand::OperationPlan_list::const_iterator i = l.begin(); i!=l.end(); ++i)
171  last = *i;
172  return last;
173 }
174 
175 
177 {
178  // Dummy call to this function
179  if (!o) return;
180 
181  // Check if it is already in the list.
182  // If it is, simply exit the function. No need to give a warning message
183  // since it's harmless.
184  for (OperationPlan_list::iterator i = deli.begin(); i!=deli.end(); ++i)
185  if (*i == o) return;
186 
187  // Add to the list of delivery operationplans. The insertion is such
188  // that the delivery list is sorted in terms of descending end time.
189  // i.e. the opplan with the latest end date is on the front of the list.
190  // Note: We're forcing resorting the deliveries with the getDelivery()
191  // method. Operation plans dates could have changed, thus disturbing the
192  // original order.
193  getDelivery();
194  OperationPlan_list::iterator j = deli.begin();
195  while (j!=deli.end() && (*j)->getDates().getEnd()>o->getDates().getEnd()) ++j;
196  deli.insert(j, o);
197 
198  // Mark the demand as being changed, so the problems can be redetected
199  setChanged();
200 
201  // Create link between operationplan and demand
202  o->setDemand(this);
203 
204  // Check validity of operation being used
205  Operation* tmpOper = getDeliveryOperation();
206  if (tmpOper && tmpOper != o->getOperation())
207  logger << "Warning: Delivery Operation '" << o->getOperation()
208  << "' different than expected '" << tmpOper
209  << "' for demand '" << this << "'" << endl;
210 }
211 
212 
214 {
215  // Operation can be specified on the demand itself,
216  if (oper) return oper;
217  // ... or on the item,
218  if (it) return it->getOperation();
219  // ... or it doesn't exist at all
220  return NULL;
221 }
222 
223 
225 {
226  double delivered(0.0);
227  for (OperationPlan_list::const_iterator i=deli.begin(); i!=deli.end(); ++i)
228  delivered += (*i)->getQuantity();
229  return delivered;
230 }
231 
232 
234 {
235  // Writing a reference
236  if (m == REFERENCE)
237  {
238  o->writeElement(tag, Tags::tag_name, getName());
239  return;
240  }
241 
242  // Write the complete object
243  if (m != NOHEADER) o->BeginObject(tag, Tags::tag_name, XMLEscape(getName()));
244 
245  // Write the fields
250  Plannable::writeElement(o, tag);
251 
254  o->writeElement(Tags::tag_due, dueDate);
256  if (getMaxLateness() != TimePeriod::MAX)
258  if (getMinShipment() != 1.0)
260 
261  // Write extra plan information
262  if (o->getContentType() == XMLOutput::PLAN
263  || o->getContentType() == XMLOutput::PLANDETAIL)
264  {
265  if (!deli.empty())
266  {
268  for (OperationPlan_list::const_iterator i=deli.begin(); i!=deli.end(); ++i)
271  }
272  if (!constraints.empty())
273  {
275  for (Problem::const_iterator i = constraints.begin(); i != constraints.end(); ++i)
278  }
279  }
280  o->EndObject(tag);
281 }
282 
283 
285 {
286  if (pAttr.isA (Tags::tag_item))
288  else if (pAttr.isA (Tags::tag_operation))
290  else if (pAttr.isA (Tags::tag_customer))
292  else if (pAttr.isA(Tags::tag_operationplan))
294  else
296 }
297 
298 
299 DECLARE_EXPORT void Demand::endElement(XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement)
300 {
301  if (pAttr.isA (Tags::tag_quantity))
302  setQuantity (pElement.getDouble());
303  else if (pAttr.isA (Tags::tag_priority))
304  setPriority (pElement.getInt());
305  else if (pAttr.isA (Tags::tag_due))
306  setDue(pElement.getDate());
307  else if (pAttr.isA (Tags::tag_operation))
308  {
309  Operation *o = dynamic_cast<Operation*>(pIn.getPreviousObject());
310  if (o) setOperation(o);
311  else throw LogicException("Incorrect object type during read operation");
312  }
313  else if (pAttr.isA (Tags::tag_customer))
314  {
315  Customer *c = dynamic_cast<Customer*>(pIn.getPreviousObject());
316  if (c) setCustomer(c);
317  else throw LogicException("Incorrect object type during read operation");
318  }
319  else if (pAttr.isA (Tags::tag_item))
320  {
321  Item *i = dynamic_cast<Item*>(pIn.getPreviousObject());
322  if (i) setItem(i);
323  else throw LogicException("Incorrect object type during read operation");
324  }
325  else if (pAttr.isA (Tags::tag_maxlateness))
326  setMaxLateness(pElement.getTimeperiod());
327  else if (pAttr.isA (Tags::tag_minshipment))
328  setMinShipment(pElement.getDouble());
329  else if (pAttr.isA(Tags::tag_operationplan))
330  {
331  OperationPlan* opplan
332  = dynamic_cast<OperationPlan*>(pIn.getPreviousObject());
333  if (opplan) addDelivery(opplan);
334  else throw LogicException("Incorrect object type during read operation");
335  }
336  else
337  {
338  Plannable::endElement(pIn, pAttr, pElement);
339  HasDescription::endElement(pIn, pAttr, pElement);
340  HasHierarchy<Demand>::endElement (pIn, pAttr, pElement);
341  }
342 }
343 
344 
346 {
347  if (attr.isA(Tags::tag_name))
348  return PythonObject(getName());
349  if (attr.isA(Tags::tag_quantity))
350  return PythonObject(getQuantity());
351  if (attr.isA(Tags::tag_due))
352  return PythonObject(getDue());
353  if (attr.isA(Tags::tag_priority))
354  return PythonObject(getPriority());
355  if (attr.isA(Tags::tag_owner))
356  return PythonObject(getOwner());
357  if (attr.isA(Tags::tag_item))
358  return PythonObject(getItem());
359  if (attr.isA(Tags::tag_customer))
360  return PythonObject(getCustomer());
361  if (attr.isA(Tags::tag_operation))
362  return PythonObject(getOperation());
363  if (attr.isA(Tags::tag_description))
364  return PythonObject(getDescription());
365  if (attr.isA(Tags::tag_category))
366  return PythonObject(getCategory());
367  if (attr.isA(Tags::tag_subcategory))
368  return PythonObject(getSubCategory());
369  if (attr.isA(Tags::tag_minshipment))
370  return PythonObject(getMinShipment());
371  if (attr.isA(Tags::tag_maxlateness))
372  return PythonObject(getMaxLateness());
373  if (attr.isA(Tags::tag_hidden))
374  return PythonObject(getHidden());
375  if (attr.isA(Tags::tag_operationplans))
376  return new DemandPlanIterator(this);
377  if (attr.isA(Tags::tag_pegging))
378  return new PeggingIterator(this);
379  if (attr.isA(Tags::tag_constraints))
380  return new ProblemIterator(*(constraints.begin()));
381  if (attr.isA(Tags::tag_members))
382  return new DemandIterator(this);
383  return NULL;
384 }
385 
386 
388 {
389  if (attr.isA(Tags::tag_name))
390  setName(field.getString());
391  else if (attr.isA(Tags::tag_priority))
392  setPriority(field.getInt());
393  else if (attr.isA(Tags::tag_quantity))
394  setQuantity(field.getDouble());
395  else if (attr.isA(Tags::tag_due))
396  setDue(field.getDate());
397  else if (attr.isA(Tags::tag_item))
398  {
399  if (!field.check(Item::metadata))
400  {
401  PyErr_SetString(PythonDataException, "demand item must be of type item");
402  return -1;
403  }
404  Item* y = static_cast<Item*>(static_cast<PyObject*>(field));
405  setItem(y);
406  }
407  else if (attr.isA(Tags::tag_customer))
408  {
409  if (!field.check(Customer::metadata))
410  {
411  PyErr_SetString(PythonDataException, "demand customer must be of type customer");
412  return -1;
413  }
414  Customer* y = static_cast<Customer*>(static_cast<PyObject*>(field));
415  setCustomer(y);
416  }
417  else if (attr.isA(Tags::tag_description))
418  setDescription(field.getString());
419  else if (attr.isA(Tags::tag_category))
420  setCategory(field.getString());
421  else if (attr.isA(Tags::tag_subcategory))
422  setSubCategory(field.getString());
423  else if (attr.isA(Tags::tag_minshipment))
424  setMinShipment(field.getDouble());
425  else if (attr.isA(Tags::tag_maxlateness))
426  setMaxLateness(field.getTimeperiod());
427  else if (attr.isA(Tags::tag_owner))
428  {
429  if (!field.check(Demand::metadata))
430  {
431  PyErr_SetString(PythonDataException, "demand owner must be of type demand");
432  return -1;
433  }
434  Demand* y = static_cast<Demand*>(static_cast<PyObject*>(field));
435  setOwner(y);
436  }
437  else if (attr.isA(Tags::tag_operation))
438  {
439  if (!field.check(Operation::metadata))
440  {
441  PyErr_SetString(PythonDataException, "demand operation must be of type operation");
442  return -1;
443  }
444  Operation* y = static_cast<Operation*>(static_cast<PyObject*>(field));
445  setOperation(y);
446  }
447  else if (attr.isA(Tags::tag_hidden))
448  setHidden(field.getBool());
449  else
450  return -1; // Error
451  return 0; // OK
452 }
453 
454 
456 {
457  // Initialize the type
459  x.setName("demandplanIterator");
460  x.setDoc("frePPLe iterator for demand delivery operationplans");
461  x.supportiter();
462  return x.typeReady();
463 }
464 
465 
466 PyObject* DemandPlanIterator::iternext()
467 {
468  if (i == dem->getDelivery().end()) return NULL;
469  PyObject* result = const_cast<OperationPlan*>(&**(i++));
470  Py_INCREF(result);
471  return result;
472 }
473 
474 } // end namespace