problem.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * *
3  * Copyright (C) 2007-2013 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 DECLARE_EXPORT bool Plannable::anyChange = false;
28 DECLARE_EXPORT bool Plannable::computationBusy = false;
43 
44 
46 {
47  // Initialize the problem metadata.
49  ("problem", "problems", NULL, Problem::writer);
51  ("problem","material excess");
53  ("problem","material shortage");
55  ("problem","excess");
57  ("problem","short");
59  ("problem","early");
61  ("problem","late");
63  ("problem","invalid data");
65  ("problem","unplanned");
67  ("problem","precedence");
69  ("problem","before fence");
71  ("problem","before current");
73  ("problem","underload");
75  ("problem","overload");
76 
77  // Initialize the Python type
79  x.setName("problem");
80  x.setDoc("frePPLe problem");
81  x.supportgetattro();
82  x.supportstr();
83  x.addMethod("toXML", toXML, METH_VARARGS, "return a XML representation");
84  const_cast<MetaCategory*>(metadata)->pythonClass = x.type_object();
85  return x.typeReady();
86 }
87 
88 
90 {
91  // 1. Sort based on entity
92  assert(owner == a.owner);
93 
94  // 2. Sort based on type
95  if (getType() != a.getType()) return getType() < a.getType();
96 
97  // 3. Sort based on start date
98  return getDates().getStart() < a.getDates().getStart();
99 }
100 
101 
103 {
104  assert(owner);
105  if ((owner->firstProblem && *this < *(owner->firstProblem))
106  || !owner->firstProblem)
107  {
108  // Insert as the first problem in the list
109  nextProblem = owner->firstProblem;
110  owner->firstProblem = this;
111  }
112  else
113  {
114  // Insert in the middle or at the end of the list
115  Problem* curProblem = owner->firstProblem->nextProblem;
116  Problem* prevProblem = owner->firstProblem;
117  while (curProblem && !(*this < *curProblem))
118  {
119  prevProblem = curProblem;
120  curProblem = curProblem->nextProblem;
121  }
122  nextProblem = curProblem;
123  prevProblem->nextProblem = this;
124  }
125 }
126 
127 
129 {
130  // Fast delete method: the code triggering this method is responsible of
131  // maintaining the problem container
132  if (!owner) return;
133 
134  if (owner->firstProblem == this)
135  // Removal from the head of the list
136  owner->firstProblem = nextProblem;
137  else
138  {
139  // Removal from the middle of the list
140  Problem *prev = owner->firstProblem;
141  for (Problem* cur = owner->firstProblem; cur; cur=cur->nextProblem)
142  {
143  if (cur == this)
144  {
145  // Found it!
146  prev->nextProblem = nextProblem;
147  return;
148  }
149  prev = cur;
150  }
151  // The problem wasn't found in the list. This shouldn't happen...
152  throw LogicException("Corrupted problem list");
153  }
154 }
155 
156 
158 {
159  if (useProblemDetection && !b)
160  // We are switching from 'yes' to 'no': delete all existing problems
161  Problem::clearProblems(*this);
162  else if (!useProblemDetection && b)
163  // We are switching from 'no' to 'yes': mark as changed for the next
164  // problem detection call
165  setChanged();
166  // Update the flag
167  useProblemDetection=b;
168 }
169 
170 
172 {
173  // Exit immediately if the list is up to date
174  if (!anyChange && !computationBusy) return;
175 
176  computationBusy = true;
177  // Get exclusive access to this function in a multi-threaded environment.
178  static Mutex computationbusy;
179  {
180  ScopeMutexLock l(computationbusy);
181 
182  // Another thread may already have computed it while this thread was
183  // waiting for the lock
184  while (anyChange)
185  {
186  // Reset to change flag. Note that during the computation the flag
187  // could be switched on again by some model change in a different thread.
188  anyChange = false;
189 
190  // Loop through all entities
192  {
193  Plannable *e = i->getEntity();
194  if (e->getChanged() && e->getDetectProblems()) i->updateProblems();
195  }
196 
197  // Mark the entities as unchanged
199  {
200  Plannable *e = j->getEntity();
201  if (e->getChanged() && e->getDetectProblems()) e->setChanged(false);
202  }
203  }
204 
205  // Unlock the exclusive access to this function
206  computationBusy = false;
207  }
208 }
209 
210 
212 {
213  // We don't bother about the mode, since this method is only called from
214  // within the writeElement() method of other classes.
215 
216  // Problem detection flag only written if different from the default value
218 }
219 
220 
221 DECLARE_EXPORT void Plannable::endElement(XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement)
222 {
223  if (pAttr.isA (Tags::tag_detectproblems))
224  {
225  bool b = pElement.getBool();
227  }
228 }
229 
230 
232 {
233  // Loop through all entities, and call clearProblems(i)
235  i != HasProblems::endEntity(); ++i)
236  {
237  clearProblems(*i);
238  i->getEntity()->setChanged(true);
239  }
240 }
241 
242 
244 {
245  // Nothing to do
246  if (!p.firstProblem) return;
247 
248  // Delete all problems in the list
249  for (Problem *cur=p.firstProblem; cur; )
250  {
251  Problem *del = cur;
252  cur = cur->nextProblem;
253  del->owner = NULL;
254  delete del;
255  }
256  p.firstProblem = NULL;
257 
258  // Mark as changed
259  if (setchanged) p.getEntity()->setChanged();
260 }
261 
262 
264 {
265  const_iterator piter = begin();
266  if (piter != end())
267  {
268  o->BeginObject(*c->grouptag);
269  for (; piter!=end(); ++piter)
270  // Note: not the regular write, but a fast write to speed things up.
271  // This is possible since problems aren't nested and are never
272  // referenced.
273  piter->writeElement(o, *c->typetag);
274  o->EndObject(*c->grouptag);
275  }
276 }
277 
278 
280 {
281  // We ignore the mode, and always write the complete model
282  o->BeginObject(tag);
283  o->writeElement(Tags::tag_name, getType().type);
285  o->writeElement(Tags::tag_start, getDates().getStart());
286  o->writeElement(Tags::tag_end, getDates().getEnd());
288  o->EndObject(tag);
289 }
290 
291 
293 {
294  // Buffer
295  bufIter = new Buffer::iterator(Buffer::begin());
296  if (*bufIter != Buffer::end()) return;
297 
298  // Move on to resource if there are no buffers
299  delete bufIter;
300  type = 1;
301  resIter = new Resource::iterator(Resource::begin());
302  if (*resIter != Resource::end()) return;
303 
304  // Move on to operationplans if there are no resources either
305  delete resIter;
306  type = 2;
308  if (*operIter != OperationPlan::end()) return;
309 
310  // Move on to demands if there are no operationplans either
311  delete operIter;
312  type = 3;
313  demIter = new Demand::iterator(Demand::begin());
314  if (*demIter == Demand::end())
315  {
316  // There is nothing at all in this model
317  delete demIter;
318  type = 4;
319  }
320 }
321 
322 
324 {
325  switch (type)
326  {
327  case 0:
328  // Buffer
329  if (*bufIter != Buffer::end())
330  if (++(*bufIter) != Buffer::end()) return *this;
331  ++type;
332  delete bufIter;
333  resIter = new Resource::iterator(Resource::begin());
334  if (*resIter != Resource::end()) return *this;
335  // Note: no break statement
336  case 1:
337  // Resource
338  if (*resIter != Resource::end())
339  if (++(*resIter) != Resource::end()) return *this;
340  ++type;
341  delete resIter;
343  if (*operIter != OperationPlan::end()) return *this;
344  // Note: no break statement
345  case 2:
346  // Operationplan
347  if (*operIter != OperationPlan::end())
348  if (++(*operIter) != OperationPlan::end()) return *this;
349  ++type;
350  delete operIter;
351  demIter = new Demand::iterator(Demand::begin());
352  if (*demIter != Demand::end()) return *this;
353  // Note: no break statement
354  case 3:
355  // Demand
356  if (*demIter != Demand::end())
357  if (++(*demIter) != Demand::end()) return *this;
358  // Ended recursing of all entities
359  ++type;
360  delete demIter;
361  demIter = NULL;
362  return *this;
363  }
364  throw LogicException("Unreachable code reached");
365 }
366 
367 
369 {
370  switch (type)
371  {
372  // Buffer
373  case 0: delete bufIter; return;
374  // Resource
375  case 1: delete resIter; return;
376  // Operation
377  case 2: delete operIter; return;
378  // Demand
379  case 3: delete demIter; return;
380  }
381 }
382 
383 
385 {
386  // Delete old iterator
387  this->~EntityIterator();
388  // Populate new values
389  type = o.type;
390  if (type==0) bufIter = new Buffer::iterator(*(o.bufIter));
391  else if (type==1) resIter = new Resource::iterator(*(o.resIter));
392  else if (type==2) operIter = new OperationPlan::iterator(*(o.operIter));
393  else if (type==3) demIter = new Demand::iterator(*(o.demIter));
394 }
395 
396 
399 {
400  // Gracefully handle self assignment
401  if (this == &o) return *this;
402  // Delete old iterator
403  this->~EntityIterator();
404  // Populate new values
405  type = o.type;
406  if (type==0) bufIter = new Buffer::iterator(*(o.bufIter));
407  else if (type==1) resIter = new Resource::iterator(*(o.resIter));
408  else if (type==2) operIter = new OperationPlan::iterator(*(o.operIter));
409  else if (type==3) demIter = new Demand::iterator(*(o.demIter));
410  return *this;
411 }
412 
413 
414 DECLARE_EXPORT bool
416 {
417  // Different iterator type, thus always different and return false
418  if (type != t.type) return true;
419 
420  // Same iterator type, more granular comparison required
421  switch (type)
422  {
423  // Buffer
424  case 0: return *bufIter != *(t.bufIter);
425  // Resource
426  case 1: return *resIter != *(t.resIter);
427  // Operationplan
428  case 2: return *operIter != *(t.operIter);
429  // Demand
430  case 3: return *demIter != *(t.demIter);
431  // Always return true for higher type numbers. This should happen only
432  // when comparing with the end of list element.
433  default: return false;
434  }
435 }
436 
437 
439 {
440  switch (type)
441  {
442  // Buffer
443  case 0: return **bufIter;
444  // Resource
445  case 1: return **resIter;
446  // Operation
447  case 2: return **operIter;
448  // Demand
449  case 3: return **demIter;
450  default: throw LogicException("Unreachable code reached");
451  }
452 }
453 
454 
456 {
457  switch (type)
458  {
459  // Buffer
460  case 0: return &**bufIter;
461  // Resource
462  case 1: return &**resIter;
463  // Operationplan
464  case 2: return &**operIter;
465  // Demand
466  case 3: return &**demIter;
467  default: throw LogicException("Unreachable code reached");
468  }
469 }
470 
471 
473 {
474  return EntityIterator();
475 }
476 
477 
479 {
480  // Note that we give call a constructor with type 4, in order to allow
481  // a fast comparison.
482  return EntityIterator(4);
483 }
484 
485 
487 {
488  // Incrementing beyond the end
489  if (!iter) return *this;
490 
491  // Move to the next problem
492  iter = iter->nextProblem;
493 
494  // Move to the next entity
495  // We need a while loop here because some entities can be without problems
496  while (!iter && !owner && eiter!=HasProblems::endEntity())
497  {
498  ++eiter;
499  if (eiter!=HasProblems::endEntity()) iter = eiter->firstProblem;
500  }
501  return *this;
502 }
503 
504 
506 {
508  return const_iterator();
509 }
510 
511 
513 {
514  // Null pointer passed, loop through the full list anyway
515  if (!i) return begin();
516 
517  // Return an iterator for a single entity
518  if (refresh) i->updateProblems();
519  return const_iterator(i);
520 }
521 
522 
524 {
525  return const_iterator(static_cast<Problem*>(NULL));
526 }
527 
528 
529 PyObject* Problem::getattro(const Attribute& attr)
530 {
531  if (attr.isA(Tags::tag_name))
532  return PythonObject(getType().type);
533  if (attr.isA(Tags::tag_description))
534  return PythonObject(getDescription());
535  if (attr.isA(Tags::tag_entity))
536  return PythonObject(getEntity());
537  if (attr.isA(Tags::tag_start))
538  return PythonObject(getDates().getStart());
539  if (attr.isA(Tags::tag_end))
540  return PythonObject(getDates().getEnd());
541  if (attr.isA(Tags::tag_weight))
542  return PythonObject(getWeight());
543  if (attr.isA(Tags::tag_owner))
544  return PythonObject(getOwner());
545  return NULL;
546 }
547 
548 
550 {
551  // Unchain the predecessor
552  if (c)
553  {
554  for (Problem *x = first; x; x = x->nextProblem)
555  if (x->nextProblem == c)
556  {
557  x->nextProblem = NULL;
558  break;
559  }
560  }
561 
562  // Delete each constraint in the list
563  for (Problem *cur = c ? c : first; cur; )
564  {
565  Problem *del = cur;
566  cur = cur->nextProblem;
567  del->owner = NULL;
568  delete del;
569  }
570 
571  // Set the header to NULL
572  if (!c) first = NULL;
573 }
574 
575 
577  const Object* o, Date st, Date nd, double w)
578 {
579  // Find the end of the list
580  Problem* cur = first;
581  while (cur && cur->nextProblem && cur->getOwner() != o)
582  cur = cur->nextProblem;
583  if (cur && cur->getOwner() == o)
584  // Duplicate problem: stop here.
585  return cur;
586 
587  // Create a new problem
588  Problem *p;
590  p = new ProblemCapacityOverload(const_cast<Resource*>(dynamic_cast<const Resource*>(o)), st, nd, w, false);
591  else if (m == ProblemMaterialShortage::metadata)
592  p = new ProblemMaterialShortage(const_cast<Buffer*>(dynamic_cast<const Buffer*>(o)), st, nd, w, false);
593  else if (m == ProblemBeforeCurrent::metadata)
594  p = new ProblemBeforeCurrent(const_cast<Operation*>(dynamic_cast<const Operation*>(o)), st, nd, w);
595  else if (m == ProblemBeforeFence::metadata)
596  p = new ProblemBeforeFence(const_cast<Operation*>(dynamic_cast<const Operation*>(o)), st, nd, w);
597  else
598  throw LogicException("Problem factory can't create this type of problem");
599 
600  // Link the problem in the list
601  if (cur)
602  cur->nextProblem = p;
603  else
604  first = p;
605  return p;
606 }
607 
608 
610 {
611  Problem *q = NULL;
612  if (p)
613  {
614  // Skip the problem that was passed as argument
615  q = p->nextProblem;
616  p->nextProblem = NULL;
617  }
618  else
619  {
620  // NULL argument: delete all
621  q = first;
622  first = NULL;
623  }
624 
625  // Delete each constraint after the marked one
626  while (q)
627  {
628  Problem *del = q;
629  q = q->nextProblem;
630  del->owner = NULL;
631  delete del;
632  }
633 }
634 
635 
637 {
638  for (Problem *p = first; p; p = p->nextProblem)
639  if (!p->nextProblem) return p;
640  return NULL;
641 }
642 
643 
644 } // End namespace