flow.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 namespace frepple
24 {
25 
31 
32 
34 {
35  // Initialize the metadata
37  ("flow", "flows", MetaCategory::ControllerDefault, writer);
38  FlowStart::metadata = new MetaClass("flow", "flow_start",
39  Object::createDefault<FlowStart>, true);
40  FlowEnd::metadata = new MetaClass("flow", "flow_end",
41  Object::createDefault<FlowEnd>);
42  FlowFixedStart::metadata = new MetaClass("flow", "flow_fixed_start",
43  Object::createDefault<FlowFixedStart>);
44  FlowFixedEnd::metadata = new MetaClass("flow", "flow_fixed_end",
45  Object::createDefault<FlowFixedEnd>);
46 
47  // Initialize the type
49  x.setName("flow");
50  x.setDoc("frePPLe flow");
51  x.supportgetattro();
52  x.supportsetattro();
53  x.supportcreate(create);
54  x.addMethod("toXML", toXML, METH_VARARGS, "return a XML representation");
55  const_cast<MetaCategory*>(metadata)->pythonClass = x.type_object();
56  return x.typeReady();
57 }
58 
59 
61 {
62  bool firstflow = true;
63  for (Operation::iterator i = Operation::begin(); i != Operation::end(); ++i)
64  for (Operation::flowlist::const_iterator j = i->getFlows().begin(); j != i->getFlows().end(); ++j)
65  {
66  if (firstflow)
67  {
69  firstflow = false;
70  }
71  // We use the FULL mode, to force the flows being written regardless
72  // of the depth in the XML tree.
74  }
75  if (!firstflow) o->EndObject(Tags::tag_flows);
76 }
77 
78 
79 DECLARE_EXPORT void Flow::validate(Action action)
80 {
81  // Catch null operation and buffer pointers
82  Operation* oper = getOperation();
83  Buffer* buf = getBuffer();
84  if (!oper || !buf)
85  {
86  // This flow is not a valid one since it misses essential information
87  if (!oper && !buf)
88  throw DataException("Missing operation and buffer on a flow");
89  else if (!oper)
90  throw DataException("Missing operation on a flow with buffer '"
91  + buf->getName() + "'");
92  else
93  throw DataException("Missing buffer on a flow with operation '"
94  + oper->getName() + "'");
95  }
96 
97  // Check if a flow with 1) identical buffer, 2) identical operation and
98  // 3) overlapping effectivity dates already exists, and 4) same
99  // flow type.
100  Operation::flowlist::const_iterator i = oper->getFlows().begin();
101  for (; i != oper->getFlows().end(); ++i)
102  if (i->getBuffer() == buf
103  && i->getEffective().overlap(getEffective())
104  && i->getType() == getType()
105  && &*i != this)
106  break;
107 
108  // Apply the appropriate action
109  switch (action)
110  {
111  case ADD:
112  if (i != oper->getFlows().end())
113  throw DataException("Flow of '" + oper->getName() + "' and '" +
114  buf->getName() + "' already exists");
115  break;
116  case CHANGE:
117  throw DataException("Can't update a flow");
118  case ADD_CHANGE:
119  // ADD is handled in the code after the switch statement
120  if (i == oper->getFlows().end()) break;
121  throw DataException("Can't update a flow");
122  case REMOVE:
123  // Delete the temporary flow object
124  delete this;
125  // Nothing to delete
126  if (i == oper->getFlows().end())
127  throw DataException("Can't remove nonexistent flow of '"
128  + oper->getName() + "' and '" + buf->getName() + "'");
129  // Delete
130  delete &*i;
131  }
132 
133  // Set a flag to make sure the level computation is triggered again
135 }
136 
137 
139 {
140  // Set a flag to make sure the level computation is triggered again
142 
143  // Delete existing flowplans
144  if (getOperation() && getBuffer())
145  {
146  // Loop over operationplans
148  // Loop over flowplans
149  for(OperationPlan::FlowPlanIterator j = i->beginFlowPlans(); j != i->endFlowPlans(); )
150  if (j->getFlow() == this) j.deleteFlowPlan();
151  else ++j;
152  }
153 
154  // Delete the flow from the operation and the buffer
155  if (getOperation()) getOperation()->flowdata.erase(this);
156  if (getBuffer()) getBuffer()->flows.erase(this);
157 
158  // Clean up alternate flows
159  if (hasAlts)
160  {
161  // The flow has alternates.
162  // Make a new flow the leading one. Or if there is only one alternate
163  // present it is not marked as an alternate any more.
164  unsigned short cnt = 0;
165  int minprio = INT_MAX;
166  Flow* newLeader = NULL;
167  for (Operation::flowlist::iterator i = getOperation()->flowdata.begin();
168  i != getOperation()->flowdata.end(); ++i)
169  if (i->altFlow == this)
170  {
171  cnt++;
172  if (i->priority < minprio)
173  {
174  newLeader = &*i;
175  minprio = i->priority;
176  }
177  }
178  if (cnt < 1)
179  throw LogicException("Alternate flows update failure");
180  else if (cnt == 1)
181  // No longer an alternate any more
182  newLeader->altFlow = NULL;
183  else
184  {
185  // Mark a new leader flow
186  newLeader->hasAlts = true;
187  newLeader->altFlow = NULL;
188  for (Operation::flowlist::iterator i = getOperation()->flowdata.begin();
189  i != getOperation()->flowdata.end(); ++i)
190  if (i->altFlow == this) i->altFlow = newLeader;
191  }
192  }
193  if (altFlow)
194  {
195  // The flow is an alternate of another one.
196  // If it was the only alternate, then the hasAlts flag on the parent
197  // flow needs to be set back to false
198  bool only_one = true;
199  for (Operation::flowlist::iterator i = getOperation()->flowdata.begin();
200  i != getOperation()->flowdata.end(); ++i)
201  if (i->altFlow == altFlow)
202  {
203  only_one = false;
204  break;
205  }
206  if (only_one) altFlow->hasAlts = false;
207  }
208 }
209 
210 
212 {
213  // Validate the argument
214  if (!f)
215  throw DataException("Setting NULL alternate flow");
216  if (hasAlts || f->altFlow)
217  throw DataException("Nested alternate flows are not allowed");
218  if (!f->isConsumer() || !isConsumer())
219  throw DataException("Only consuming alternate flows are supported");
220 
221  // Update both flows
222  f->hasAlts = true;
223  altFlow = f;
224 }
225 
226 
227 DECLARE_EXPORT void Flow::setAlternate(const string& n)
228 {
229  if (!getOperation())
230  throw LogicException("Can't set an alternate flow before setting the operation");
231  Flow *x = getOperation()->flowdata.find(n);
232  if (!x) throw DataException("Can't find flow with name '" + n + "'");
233  setAlternate(x);
234 }
235 
236 
238 {
239  // If the flow has already been saved, no need to repeat it again
240  // A 'reference' to a flow is not useful to be saved.
241  if (m == REFERENCE) return;
242  assert(m != NOHEAD);
243 
244  // Write the head
245  o->BeginObject(tag, Tags::tag_type, getType().type);
246 
247  // If the flow is defined inside of an operation tag, we don't need to save
248  // the operation. Otherwise we do save it...
249  if (!dynamic_cast<Operation*>(o->getPreviousObject()))
251 
252  // If the flow is defined inside of an buffer tag, we don't need to save
253  // the buffer. Otherwise we do save it...
254  if (!dynamic_cast<Buffer*>(o->getPreviousObject()))
256 
257  // Write the quantity, priority, name and alternate
260  if (!getName().empty()) o->writeElement(Tags::tag_name, getName());
261  if (getAlternate())
263 
264  // Write the effective daterange
265  if (getEffective().getStart() != Date::infinitePast)
267  if (getEffective().getEnd() != Date::infiniteFuture)
269 
270  // Write the tail
271  if (m != NOHEADTAIL && m != NOTAIL) o->EndObject(tag);
272 }
273 
274 
276 {
277  if (pAttr.isA (Tags::tag_buffer))
279  else if (pAttr.isA (Tags::tag_operation))
281 }
282 
283 
284 DECLARE_EXPORT void Flow::endElement (XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement)
285 {
286  if (pAttr.isA (Tags::tag_buffer))
287  {
288  Buffer * b = dynamic_cast<Buffer*>(pIn.getPreviousObject());
289  if (b) setBuffer(b);
290  else throw LogicException("Incorrect object type during read operation");
291  }
292  else if (pAttr.isA (Tags::tag_operation))
293  {
294  Operation * o = dynamic_cast<Operation*>(pIn.getPreviousObject());
295  if (o) setOperation(o);
296  else throw LogicException("Incorrect object type during read operation");
297  }
298  else if (pAttr.isA(Tags::tag_quantity))
299  setQuantity(pElement.getDouble());
300  else if (pAttr.isA(Tags::tag_priority))
301  setPriority(pElement.getInt());
302  else if (pAttr.isA(Tags::tag_name))
303  setName(pElement.getString());
304  else if (pAttr.isA(Tags::tag_alternate))
305  setAlternate(pElement.getString());
306  else if (pAttr.isA(Tags::tag_search))
307  setSearch(pElement.getString());
308  else if (pAttr.isA(Tags::tag_action))
309  {
310  delete static_cast<Action*>(pIn.getUserArea());
311  pIn.setUserArea(
312  new Action(MetaClass::decodeAction(pElement.getString().c_str()))
313  );
314  }
315  else if (pAttr.isA(Tags::tag_effective_end))
316  setEffectiveEnd(pElement.getDate());
317  else if (pAttr.isA(Tags::tag_effective_start))
318  setEffectiveStart(pElement.getDate());
319  else if (pIn.isObjectEnd())
320  {
321  // The flow data are now all read in. See if it makes sense now...
322  Action a = pIn.getUserArea() ?
323  *static_cast<Action*>(pIn.getUserArea()) :
324  ADD_CHANGE;
325  delete static_cast<Action*>(pIn.getUserArea());
326  try { validate(a); }
327  catch (...)
328  {
329  delete this;
330  throw;
331  }
332  }
333 }
334 
335 
337 (XMLOutput *o, const Keyword& tag, mode m) const
338 {
339  // If the flow has already been saved, no need to repeat it again
340  // A 'reference' to a flow is not useful to be saved.
341  if (m == REFERENCE) return;
342  assert(m != NOHEAD);
343 
344  // Write the head
345  o->BeginObject(tag, Tags::tag_type, getType().type);
346 
347  // If the flow is defined inside of an operation tag, we don't need to save
348  // the operation. Otherwise we do save it...
349  if (!dynamic_cast<Operation*>(o->getPreviousObject()))
350  o->writeElement(Tags::tag_operation, getOperation());
351 
352  // If the flow is defined inside of an buffer tag, we don't need to save
353  // the buffer. Otherwise we do save it...
354  if (!dynamic_cast<Buffer*>(o->getPreviousObject()))
355  o->writeElement(Tags::tag_buffer, getBuffer());
356 
357  // Write the quantity, priority name and alternate
358  o->writeElement(Tags::tag_quantity, getQuantity());
359  if (getPriority()!=1) o->writeElement(Tags::tag_priority, getPriority());
360  if (!getName().empty()) o->writeElement(Tags::tag_name, getName());
361  if (getAlternate())
362  o->writeElement(Tags::tag_alternate, getAlternate()->getName());
363 
364  // Write the effective daterange
365  if (getEffective().getStart() != Date::infinitePast)
366  o->writeElement(Tags::tag_effective_start, getEffective().getStart());
367  if (getEffective().getEnd() != Date::infiniteFuture)
368  o->writeElement(Tags::tag_effective_end, getEffective().getEnd());
369 
370  // Write the tail
371  if (m != NOHEADTAIL && m != NOTAIL) o->EndObject(tag);
372 }
373 
374 
375 DECLARE_EXPORT PyObject* Flow::getattro(const Attribute& attr)
376 {
377  if (attr.isA(Tags::tag_buffer))
378  return PythonObject(getBuffer());
379  if (attr.isA(Tags::tag_operation))
380  return PythonObject(getOperation());
381  if (attr.isA(Tags::tag_quantity))
382  return PythonObject(getQuantity());
383  if (attr.isA(Tags::tag_priority))
384  return PythonObject(getPriority());
385  if (attr.isA(Tags::tag_effective_end))
386  return PythonObject(getEffective().getEnd());
387  if (attr.isA(Tags::tag_effective_start))
388  return PythonObject(getEffective().getStart());
389  if (attr.isA(Tags::tag_name))
390  return PythonObject(getName());
391  if (attr.isA(Tags::tag_hidden))
392  return PythonObject(getHidden());
393  if (attr.isA(Tags::tag_alternate))
394  return PythonObject(getAlternate());
395  if (attr.isA(Tags::tag_search))
396  {
397  ostringstream ch;
398  ch << getSearch();
399  return PythonObject(ch.str());
400  }
401  return NULL;
402 }
403 
404 
405 DECLARE_EXPORT int Flow::setattro(const Attribute& attr, const PythonObject& field)
406 {
407  if (attr.isA(Tags::tag_buffer))
408  {
409  if (!field.check(Buffer::metadata))
410  {
411  PyErr_SetString(PythonDataException, "flow buffer must be of type buffer");
412  return -1;
413  }
414  Buffer* y = static_cast<Buffer*>(static_cast<PyObject*>(field));
415  setBuffer(y);
416  }
417  else if (attr.isA(Tags::tag_operation))
418  {
419  if (!field.check(Operation::metadata))
420  {
421  PyErr_SetString(PythonDataException, "flow operation must be of type operation");
422  return -1;
423  }
424  Operation* y = static_cast<Operation*>(static_cast<PyObject*>(field));
425  setOperation(y);
426  }
427  else if (attr.isA(Tags::tag_quantity))
428  setQuantity(field.getDouble());
429  else if (attr.isA(Tags::tag_priority))
430  setPriority(field.getInt());
431  else if (attr.isA(Tags::tag_effective_end))
432  setEffectiveEnd(field.getDate());
433  else if (attr.isA(Tags::tag_effective_start))
434  setEffectiveStart(field.getDate());
435  else if (attr.isA(Tags::tag_name))
436  setName(field.getString());
437  else if (attr.isA(Tags::tag_alternate))
438  {
439  if (!field.check(Flow::metadata))
440  setAlternate(field.getString());
441  else
442  {
443  Flow *y = static_cast<Flow*>(static_cast<PyObject*>(field));
444  setAlternate(y);
445  }
446  }
447  else if (attr.isA(Tags::tag_search))
448  setSearch(field.getString());
449  else
450  return -1;
451  return 0;
452 }
453 
454 
455 /** @todo method implementation not generic and doesn't support clean subclassing. */
456 PyObject* Flow::create(PyTypeObject* pytype, PyObject* args, PyObject* kwds)
457 {
458  try
459  {
460  // Pick up the operation
461  PyObject* oper = PyDict_GetItemString(kwds,"operation");
462  if (!PyObject_TypeCheck(oper, Operation::metadata->pythonClass))
463  throw DataException("flow operation must be of type operation");
464 
465  // Pick up the resource
466  PyObject* buf = PyDict_GetItemString(kwds,"buffer");
467  if (!PyObject_TypeCheck(buf, Buffer::metadata->pythonClass))
468  throw DataException("flow buffer must be of type buffer");
469 
470  // Pick up the quantity
471  PyObject* q1 = PyDict_GetItemString(kwds,"quantity");
472  double q2 = q1 ? PythonObject(q1).getDouble() : 1.0;
473 
474  // Pick up the effectivity dates
475  DateRange eff;
476  PyObject* eff_start = PyDict_GetItemString(kwds,"effective_start");
477  if (eff_start)
478  {
479  PythonObject d(eff_start);
480  eff.setStart(d.getDate());
481  }
482  PyObject* eff_end = PyDict_GetItemString(kwds,"effective_end");
483  if (eff_end)
484  {
485  PythonObject d(eff_end);
486  eff.setEnd(d.getDate());
487  }
488 
489  // Pick up the type and create the flow
490  Flow *l;
491  PyObject* t = PyDict_GetItemString(kwds,"type");
492  if (t)
493  {
494  PythonObject d(t);
495  if (d.getString() == "flow_end")
496  l = new FlowEnd(
497  static_cast<Operation*>(oper),
498  static_cast<Buffer*>(buf),
499  q2, eff
500  );
501  else if (d.getString() == "flow_fixed_end")
502  l = new FlowFixedEnd(
503  static_cast<Operation*>(oper),
504  static_cast<Buffer*>(buf),
505  q2, eff
506  );
507  else if (d.getString() == "flow_fixed_start")
508  l = new FlowFixedStart(
509  static_cast<Operation*>(oper),
510  static_cast<Buffer*>(buf),
511  q2, eff
512  );
513  else
514  l = new FlowStart(
515  static_cast<Operation*>(oper),
516  static_cast<Buffer*>(buf),
517  q2, eff
518  );
519  }
520  else
521  l = new FlowStart(
522  static_cast<Operation*>(oper),
523  static_cast<Buffer*>(buf),
524  q2, eff
525  );
526 
527  // Return the object
528  Py_INCREF(l);
529  return static_cast<PyObject*>(l);
530  }
531  catch (...)
532  {
533  PythonType::evalException();
534  return NULL;
535  }
536 }
537 
538 
540 {
541  // Initialize the type
543  x.setName("flowIterator");
544  x.setDoc("frePPLe iterator for flows");
545  x.supportiter();
546  return x.typeReady();
547 }
548 
549 
550 PyObject* FlowIterator::iternext()
551 {
552  PyObject* result;
553  if (buf)
554  {
555  // Iterate over flows on a buffer
556  if (ib == buf->getFlows().end()) return NULL;
557  result = const_cast<Flow*>(&*ib);
558  ++ib;
559  }
560  else
561  {
562  // Iterate over flows on an operation
563  if (io == oper->getFlows().end()) return NULL;
564  result = const_cast<Flow*>(&*io);
565  ++io;
566  }
567  Py_INCREF(result);
568  return result;
569 }
570 
571 } // end namespace