loadplan.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 
28 
29 
31 {
32  // Initialize the metadata
33  metadata = new MetaCategory("loadplan", "loadplans");
34 
35  // Initialize the Python type
37  x.setName("loadplan");
38  x.setDoc("frePPLe loadplan");
39  x.supportgetattro();
40  x.supportsetattro();
41  const_cast<MetaCategory*>(metadata)->pythonClass = x.type_object();
42  return x.typeReady();
43 }
44 
45 
47 {
48  assert(o);
49  ld = const_cast<Load*>(r);
50  oper = o;
51  start_or_end = START;
52 
53  // Update the resource field
54  res = r->getResource();
55 
56  // Add to the operationplan
57  nextLoadPlan = NULL;
58  if (o->firstloadplan)
59  {
60  // Append to the end
61  LoadPlan *c = o->firstloadplan;
62  while (c->nextLoadPlan) c = c->nextLoadPlan;
63  c->nextLoadPlan = this;
64  }
65  else
66  // First in the list
67  o->firstloadplan = this;
68 
69  // Insert in the resource timeline
70  getResource()->loadplans.insert(
71  this,
72  ld->getLoadplanQuantity(this),
73  ld->getLoadplanDate(this)
74  );
75 
76  // Initialize the Python type
78 
79  // Create a loadplan to mark the end of the operationplan.
80  new LoadPlan(o, r, this);
81 
82  // Mark the operation and resource as being changed. This will trigger
83  // the recomputation of their problems
85  r->getOperation()->setChanged();
86 }
87 
88 
90 {
91  ld = const_cast<Load*>(r);
92  oper = o;
93  start_or_end = END;
94 
95  // Update the resource field
96  res = lp->getResource();
97 
98  // Add to the operationplan
99  nextLoadPlan = NULL;
100  if (o->firstloadplan)
101  {
102  // Append to the end
103  LoadPlan *c = o->firstloadplan;
104  while (c->nextLoadPlan) c = c->nextLoadPlan;
105  c->nextLoadPlan = this;
106  }
107  else
108  // First in the list
109  o->firstloadplan = this;
110 
111  // Insert in the resource timeline
112  getResource()->loadplans.insert(
113  this,
114  ld->getLoadplanQuantity(this),
115  ld->getLoadplanDate(this)
116  );
117 
118  // Initialize the Python type
120 }
121 
122 
124 {
125  // Nothing to do
126  if (res == newres) return;
127 
128  // Validate the argument
129  if (!newres) throw DataException("Can't switch to NULL resource");
130  if (check)
131  {
132  // New resource must be a subresource of the load's resource.
133  bool ok = false;
134  for (const Resource* i = newres; i && !ok; i = i->getOwner())
135  if (i == getLoad()->getResource()) ok = true;
136  if (!ok)
137  throw DataException("Resource isn't matching the resource specified on the load");
138 
139  // New resource must have the required skill
140  if (getLoad()->getSkill())
141  {
142  ok = false;
143  for(Resource::skilllist::const_iterator s = newres->getSkills().begin();
144  s != newres->getSkills().end() && !ok; s++)
145  if (s->getSkill() == getLoad()->getSkill()) ok = true;
146  if (!ok)
147  throw DataException("Resource misses the skill specified on the load");
148  }
149  }
150 
151  // Mark entities as changed
152  if (oper) oper->getOperation()->setChanged();
153  if (res && res!=newres) res->setChanged();
154  newres->setChanged();
155 
156  // Update also the setup operationplans
157  if (oper && oper->getOperation() != OperationSetup::setupoperation)
158  {
159  bool oldHasSetup = ld && !ld->getSetup().empty() // TODO not fully correct. If the load is changed, it is still possible that the old load had a setup, while ld doesn't have one any more...
160  && res && res->getSetupMatrix();
161  bool newHasSetup = ld && !ld->getSetup().empty()
162  && newres->getSetupMatrix();
163  OperationPlan *setupOpplan = NULL;
164  if (oldHasSetup)
165  {
166  for (OperationPlan::iterator i(oper); i != oper->end(); ++i)
167  if (i->getOperation() == OperationSetup::setupoperation)
168  {
169  setupOpplan = &*i;
170  break;
171  }
172  if (!setupOpplan) oldHasSetup = false;
173  }
174  if (oldHasSetup)
175  {
176  if (newHasSetup)
177  {
178  // Case 1: Both the old and new load require a setup
179  LoadPlan *setupLdplan = NULL;
180  for (OperationPlan::LoadPlanIterator j = setupOpplan->beginLoadPlans();
181  j != setupOpplan->endLoadPlans(); ++j)
182  if (j->getLoad() == ld)
183  {
184  setupLdplan = &*j;
185  break;
186  }
187  if (!setupLdplan)
188  throw LogicException("Can't find loadplan on setup operationplan");
189  // Update the loadplan
190  setupOpplan->setEnd(setupOpplan->getDates().getEnd());
191  }
192  else
193  {
194  // Case 2: Delete the old setup which is not required any more
195  oper->eraseSubOperationPlan(setupOpplan);
196  }
197  }
198  else
199  {
200  if (newHasSetup)
201  {
202  // Case 3: Create a new setup operationplan
204  1, Date::infinitePast, oper->getDates().getEnd(), NULL, oper);
205  }
206  //else:
207  // Case 4: No setup for the old or new load
208  }
209  }
210 
211  // Find the loadplan before the setup
212  LoadPlan *prevldplan = NULL;
214  {
216  i != getResource()->getLoadPlans().end(); --i)
217  {
218  const LoadPlan *l = dynamic_cast<const LoadPlan*>(&*i);
219  if (l && l->getOperationPlan() != getOperationPlan()
221  && !l->isStart())
222  {
223  prevldplan = const_cast<LoadPlan*>(l);
224  break;
225  }
226  }
227  if (!prevldplan)
228  {
230  i != getResource()->getLoadPlans().end(); ++i)
231  {
232  const LoadPlan *l = dynamic_cast<const LoadPlan*>(&*i);
233  if (l && l->getOperationPlan() != getOperationPlan()
235  && !l->isStart())
236  {
237  prevldplan = const_cast<LoadPlan*>(l);
238  break;
239  }
240  }
241  }
242  }
243 
244  // Change this loadplan and its brother
245  for (LoadPlan *ldplan = getOtherLoadPlan(); true; )
246  {
247  // Remove from the old resource, if there is one
248  if (res)
249  {
250  res->loadplans.erase(ldplan);
251  res->setChanged();
252  }
253 
254  // Insert in the new resource.
255  // This code assumes the date and quantity of the loadplan don't change
256  // when a new resource is assigned.
257  ldplan->res = newres;
258  newres->loadplans.insert(
259  ldplan,
260  ld->getLoadplanQuantity(ldplan),
261  ld->getLoadplanDate(ldplan)
262  );
263 
264  // Repeat for the brother loadplan or exit
265  if (ldplan != this) ldplan = this;
266  else break;
267  }
268 
269  // Update the setups on the old resource
270  if (prevldplan) prevldplan->res->updateSetups(prevldplan);
271 
272  // Change the resource
273  newres->setChanged();
274 }
275 
276 
278 {
279  for (LoadPlan *i = oper->firstloadplan; i; i = i->nextLoadPlan)
280  if (i->ld == ld && i != this) return i;
281  throw LogicException("No matching loadplan found");
282 }
283 
284 
286 {
287  // Update the timeline data structure
289  this,
290  ld->getLoadplanQuantity(this),
291  ld->getLoadplanDate(this)
292  );
293 
294  // Review adjacent setups
295  if (!isStart()) getResource()->updateSetups(this);
296 
297  // Mark the operation and resource as being changed. This will trigger
298  // the recomputation of their problems
299  getResource()->setChanged();
300  ld->getOperation()->setChanged();
301 }
302 
303 
304 DECLARE_EXPORT const string& LoadPlan::getSetup(bool current) const
305 {
306  // This resource has no setupmatrix
307  static string nosetup;
308  assert(ld);
309  if (!getResource()->getSetupMatrix()) return nosetup;
310 
311  // Current load has a setup
312  if (!ld->getSetup().empty() && current) return ld->getSetup();
313 
314  // Scan earlier setups
315  for (Resource::loadplanlist::const_iterator i(this);
316  i != getResource()->getLoadPlans().end(); --i)
317  {
318  const LoadPlan* j = dynamic_cast<const LoadPlan*>(&*i);
319  if (j && !j->getLoad()->getSetup().empty() && (current || j != this))
320  return j->getLoad()->getSetup();
321  }
322 
323  // No conversions found - return the original setup
324  return getResource()->getSetup();
325 }
326 
327 
329 {
330  getResource()->setChanged();
331  LoadPlan *prevldplan = NULL;
333  {
335  i != getResource()->getLoadPlans().end(); --i)
336  {
337  const LoadPlan *l = dynamic_cast<const LoadPlan*>(&*i);
338  if (l && l->getOperationPlan() != getOperationPlan()
340  && !l->isStart())
341  {
342  prevldplan = const_cast<LoadPlan*>(l);
343  break;
344  }
345  }
346  if (!prevldplan)
347  {
349  i != getResource()->getLoadPlans().end(); ++i)
350  {
351  const LoadPlan *l = dynamic_cast<const LoadPlan*>(&*i);
352  if (l && l->getOperationPlan() != getOperationPlan()
354  && !l->isStart())
355  {
356  prevldplan = const_cast<LoadPlan*>(l);
357  break;
358  }
359  }
360  }
361  }
362  getResource()->loadplans.erase(this);
363  if (prevldplan) getResource()->updateSetups(prevldplan);
364 }
365 
366 
368 {
369  // No change
370  if (newld == ld) return;
371 
372  // Verify the data
373  if (!newld) throw DataException("Can't switch to NULL load");
374  if (ld && ld->getOperation() != newld->getOperation())
375  throw DataException("Only switching to a load on the same operation is allowed");
376 
377  // Update the load and resource fields
378  LoadPlan* o = getOtherLoadPlan();
379  if (o) o->ld = newld;
380  ld = newld;
381  setResource(newld->getResource());
382 }
383 
384 
385 PyObject* LoadPlan::getattro(const Attribute& attr)
386 {
387  if (attr.isA(Tags::tag_operationplan))
388  return PythonObject(getOperationPlan());
389  if (attr.isA(Tags::tag_quantity))
390  return PythonObject(getQuantity());
391  if (attr.isA(Tags::tag_startdate))
392  return PythonObject(getDate());
393  if (attr.isA(Tags::tag_enddate))
395  if (attr.isA(Tags::tag_resource))
396  return PythonObject(getResource());
397  if (attr.isA(Tags::tag_operation)) // Convenient shortcut
398  return PythonObject(getLoad()->getOperation());
399  if (attr.isA(Tags::tag_load))
400  return PythonObject(getLoad());
401  if (attr.isA(Tags::tag_onhand))
402  return PythonObject(getOnhand());
403  if (attr.isA(Tags::tag_setup))
404  return PythonObject(getSetup());
405  return NULL;
406 }
407 
408 
410 {
411  if (attr.isA(Tags::tag_resource))
412  {
413  if (!field.check(Resource::metadata))
414  {
415  PyErr_SetString(PythonDataException, "loadplan resource must be of type resource");
416  return -1;
417  }
418  Resource* y = static_cast<Resource*>(static_cast<PyObject*>(field));
419  setResource(y, true);
420  }
421  else if (attr.isA(Tags::tag_load))
422  {
423  if (!field.check(Load::metadata))
424  {
425  PyErr_SetString(PythonDataException, "loadplan load must be of type load");
426  return -1;
427  }
428  Load* y = static_cast<Load*>(static_cast<PyObject*>(field));
429  setLoad(y);
430  }
431  else
432  return -1;
433  return 0;
434 }
435 
436 
438 {
439  // Initialize the type
441  x.setName("loadplanIterator");
442  x.setDoc("frePPLe iterator for loadplan");
443  x.supportiter();
444  return x.typeReady();
445 }
446 
447 
448 PyObject* LoadPlanIterator::iternext()
449 {
450  LoadPlan* ld;
451  if (resource_or_opplan)
452  {
453  // Skip zero quantity loadplans
454  while (*resiter != res->getLoadPlans().end() && (*resiter)->getQuantity()==0.0)
455  ++(*resiter);
456  if (*resiter == res->getLoadPlans().end()) return NULL;
457 
458  // Return result
459  ld = const_cast<LoadPlan*>(static_cast<const LoadPlan*>(&*((*resiter)++)));
460  }
461  else
462  {
463  while (*opplaniter != opplan->endLoadPlans() && (*opplaniter)->getQuantity()==0.0)
464  ++(*opplaniter);
465  if (*opplaniter == opplan->endLoadPlans()) return NULL;
466  ld = static_cast<LoadPlan*>(&*((*opplaniter)++));
467  }
468  Py_INCREF(ld);
469  return const_cast<LoadPlan*>(ld);
470 }
471 
472 } // end namespace