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