flow.cpp
Go to the documentation of this file.
00001 /*************************************************************************** 00002 file : $URL: http://svn.code.sf.net/p/frepple/code/trunk/src/model/flow.cpp $ 00003 version : $LastChangedRevision: 1713 $ $LastChangedBy: jdetaeye $ 00004 date : $LastChangedDate: 2012-07-18 11:46:01 +0200 (Wed, 18 Jul 2012) $ 00005 ***************************************************************************/ 00006 00007 /*************************************************************************** 00008 * * 00009 * Copyright (C) 2007-2012 by Johan De Taeye, frePPLe bvba * 00010 * * 00011 * This library is free software; you can redistribute it and/or modify it * 00012 * under the terms of the GNU Affero General Public License as published * 00013 * by the Free Software Foundation; either version 3 of the License, or * 00014 * (at your option) any later version. * 00015 * * 00016 * This library is distributed in the hope that it will be useful, * 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00019 * GNU Affero General Public License for more details. * 00020 * * 00021 * You should have received a copy of the GNU Affero General Public * 00022 * License along with this program. * 00023 * If not, see <http://www.gnu.org/licenses/>. * 00024 * * 00025 ***************************************************************************/ 00026 00027 #define FREPPLE_CORE 00028 #include "frepple/model.h" 00029 namespace frepple 00030 { 00031 00032 DECLARE_EXPORT const MetaCategory* Flow::metadata; 00033 DECLARE_EXPORT const MetaClass* FlowStart::metadata, 00034 *FlowEnd::metadata; 00035 00036 00037 int Flow::initialize() 00038 { 00039 // Initialize the metadata 00040 metadata = new MetaCategory 00041 ("flow", "flows", MetaCategory::ControllerDefault, writer); 00042 FlowStart::metadata = new MetaClass("flow", "flow_start", 00043 Object::createDefault<FlowStart>, true); 00044 FlowEnd::metadata = new MetaClass("flow", "flow_end", 00045 Object::createDefault<FlowEnd>); 00046 00047 // Initialize the type 00048 PythonType& x = FreppleCategory<Flow>::getType(); 00049 x.setName("flow"); 00050 x.setDoc("frePPLe flow"); 00051 x.supportgetattro(); 00052 x.supportsetattro(); 00053 x.supportcreate(create); 00054 x.addMethod("toXML", toXML, METH_VARARGS, "return a XML representation"); 00055 const_cast<MetaCategory*>(metadata)->pythonClass = x.type_object(); 00056 return x.typeReady(); 00057 } 00058 00059 00060 void Flow::writer(const MetaCategory* c, XMLOutput* o) 00061 { 00062 bool firstflow = true; 00063 for (Operation::iterator i = Operation::begin(); i != Operation::end(); ++i) 00064 for (Operation::flowlist::const_iterator j = i->getFlows().begin(); j != i->getFlows().end(); ++j) 00065 { 00066 if (firstflow) 00067 { 00068 o->BeginObject(Tags::tag_flows); 00069 firstflow = false; 00070 } 00071 // We use the FULL mode, to force the flows being written regardless 00072 // of the depth in the XML tree. 00073 o->writeElement(Tags::tag_flow, &*j, FULL); 00074 } 00075 if (!firstflow) o->EndObject(Tags::tag_flows); 00076 } 00077 00078 00079 DECLARE_EXPORT void Flow::validate(Action action) 00080 { 00081 // Catch null operation and buffer pointers 00082 Operation* oper = getOperation(); 00083 Buffer* buf = getBuffer(); 00084 if (!oper || !buf) 00085 { 00086 // This flow is not a valid one since it misses essential information 00087 if (!oper && !buf) 00088 throw DataException("Missing operation and buffer on a flow"); 00089 else if (!oper) 00090 throw DataException("Missing operation on a flow with buffer '" 00091 + buf->getName() + "'"); 00092 else 00093 throw DataException("Missing buffer on a flow with operation '" 00094 + oper->getName() + "'"); 00095 } 00096 00097 // Check if a flow with 1) identical buffer, 2) identical operation and 00098 // 3) overlapping effectivity dates already exists 00099 Operation::flowlist::const_iterator i = oper->getFlows().begin(); 00100 for (; i != oper->getFlows().end(); ++i) 00101 if (i->getBuffer() == buf 00102 && i->getEffective().overlap(getEffective()) 00103 && &*i != this) 00104 break; 00105 00106 // Apply the appropriate action 00107 switch (action) 00108 { 00109 case ADD: 00110 if (i != oper->getFlows().end()) 00111 throw DataException("Flow of '" + oper->getName() + "' and '" + 00112 buf->getName() + "' already exists"); 00113 break; 00114 case CHANGE: 00115 throw DataException("Can't update a flow"); 00116 case ADD_CHANGE: 00117 // ADD is handled in the code after the switch statement 00118 if (i == oper->getFlows().end()) break; 00119 throw DataException("Can't update a flow"); 00120 case REMOVE: 00121 // Delete the temporary flow object 00122 delete this; 00123 // Nothing to delete 00124 if (i == oper->getFlows().end()) 00125 throw DataException("Can't remove nonexistent flow of '" 00126 + oper->getName() + "' and '" + buf->getName() + "'"); 00127 // Delete 00128 delete &*i; 00129 } 00130 00131 // Attach to buffers higher up in the hierarchy 00132 // Note that the owner can create more loads if it has an owner too. 00133 if (buf->hasOwner() && action!=REMOVE) new Flow(oper, buf->getOwner(), quantity); 00134 00135 // Set a flag to make sure the level computation is triggered again 00136 HasLevel::triggerLazyRecomputation(); 00137 } 00138 00139 00140 DECLARE_EXPORT Flow::~Flow() 00141 { 00142 // Set a flag to make sure the level computation is triggered again 00143 HasLevel::triggerLazyRecomputation(); 00144 00145 // Delete existing flowplans 00146 if (getOperation() && getBuffer()) 00147 { 00148 // Loop over operationplans 00149 for(OperationPlan::iterator i(getOperation()); i != OperationPlan::end(); ++i) 00150 // Loop over flowplans 00151 for(OperationPlan::FlowPlanIterator j = i->beginFlowPlans(); j != i->endFlowPlans(); ) 00152 if (j->getFlow() == this) j.deleteFlowPlan(); 00153 else ++j; 00154 } 00155 00156 // Delete the flow from the operation and the buffer 00157 if (getOperation()) getOperation()->flowdata.erase(this); 00158 if (getBuffer()) getBuffer()->flows.erase(this); 00159 00160 // Clean up alternate flows 00161 if (hasAlts) 00162 { 00163 // The flow has alternates. 00164 // Make a new flow the leading one. Or if there is only one alternate 00165 // present it is not marked as an alternate any more. 00166 unsigned short cnt = 0; 00167 int minprio = INT_MAX; 00168 Flow* newLeader = NULL; 00169 for (Operation::flowlist::iterator i = getOperation()->flowdata.begin(); 00170 i != getOperation()->flowdata.end(); ++i) 00171 if (i->altFlow == this) 00172 { 00173 cnt++; 00174 if (i->priority < minprio) 00175 { 00176 newLeader = &*i; 00177 minprio = i->priority; 00178 } 00179 } 00180 if (cnt < 1) 00181 throw LogicException("Alternate flows update failure"); 00182 else if (cnt == 1) 00183 // No longer an alternate any more 00184 newLeader->altFlow = NULL; 00185 else 00186 { 00187 // Mark a new leader flow 00188 newLeader->hasAlts = true; 00189 newLeader->altFlow = NULL; 00190 for (Operation::flowlist::iterator i = getOperation()->flowdata.begin(); 00191 i != getOperation()->flowdata.end(); ++i) 00192 if (i->altFlow == this) i->altFlow = newLeader; 00193 } 00194 } 00195 if (altFlow) 00196 { 00197 // The flow is an alternate of another one. 00198 // If it was the only alternate, then the hasAlts flag on the parent 00199 // flow needs to be set back to false 00200 bool only_one = true; 00201 for (Operation::flowlist::iterator i = getOperation()->flowdata.begin(); 00202 i != getOperation()->flowdata.end(); ++i) 00203 if (i->altFlow == altFlow) 00204 { 00205 only_one = false; 00206 break; 00207 } 00208 if (only_one) altFlow->hasAlts = false; 00209 } 00210 } 00211 00212 00213 DECLARE_EXPORT void Flow::setAlternate(Flow *f) 00214 { 00215 // Validate the argument 00216 if (!f) 00217 throw DataException("Setting NULL alternate flow"); 00218 if (hasAlts || f->altFlow) 00219 throw DataException("Nested alternate flows are not allowed"); 00220 if (!f->isConsumer() || !isConsumer()) 00221 throw DataException("Only consuming alternate flows are supported"); 00222 00223 // Update both flows 00224 f->hasAlts = true; 00225 altFlow = f; 00226 } 00227 00228 00229 DECLARE_EXPORT void Flow::setAlternate(const string& n) 00230 { 00231 if (!getOperation()) 00232 throw LogicException("Can't set an alternate flow before setting the operation"); 00233 Flow *x = getOperation()->flowdata.find(n); 00234 if (!x) throw DataException("Can't find flow with name '" + n + "'"); 00235 setAlternate(x); 00236 } 00237 00238 00239 DECLARE_EXPORT void Flow::writeElement (XMLOutput *o, const Keyword& tag, mode m) const 00240 { 00241 // If the flow has already been saved, no need to repeat it again 00242 // A 'reference' to a flow is not useful to be saved. 00243 if (m == REFERENCE) return; 00244 assert(m != NOHEADER); 00245 00246 // Write the header 00247 o->BeginObject(tag, Tags::tag_type, getType().type); 00248 00249 // If the flow is defined inside of an operation tag, we don't need to save 00250 // the operation. Otherwise we do save it... 00251 if (!dynamic_cast<Operation*>(o->getPreviousObject())) 00252 o->writeElement(Tags::tag_operation, getOperation()); 00253 00254 // If the flow is defined inside of an buffer tag, we don't need to save 00255 // the buffer. Otherwise we do save it... 00256 if (!dynamic_cast<Buffer*>(o->getPreviousObject())) 00257 o->writeElement(Tags::tag_buffer, getBuffer()); 00258 00259 // Write the quantity, priority, name and alternate 00260 o->writeElement(Tags::tag_quantity, getQuantity()); 00261 if (getPriority()!=1) o->writeElement(Tags::tag_priority, getPriority()); 00262 if (!getName().empty()) o->writeElement(Tags::tag_name, getName()); 00263 if (getAlternate()) 00264 o->writeElement(Tags::tag_alternate, getAlternate()->getName()); 00265 00266 // Write the effective daterange 00267 if (getEffective().getStart() != Date::infinitePast) 00268 o->writeElement(Tags::tag_effective_start, getEffective().getStart()); 00269 if (getEffective().getEnd() != Date::infiniteFuture) 00270 o->writeElement(Tags::tag_effective_end, getEffective().getEnd()); 00271 00272 // End of flow object 00273 o->EndObject(tag); 00274 } 00275 00276 00277 DECLARE_EXPORT void Flow::beginElement(XMLInput& pIn, const Attribute& pAttr) 00278 { 00279 if (pAttr.isA (Tags::tag_buffer)) 00280 pIn.readto( Buffer::reader(Buffer::metadata,pIn.getAttributes()) ); 00281 else if (pAttr.isA (Tags::tag_operation)) 00282 pIn.readto( Operation::reader(Operation::metadata,pIn.getAttributes()) ); 00283 } 00284 00285 00286 DECLARE_EXPORT void Flow::endElement (XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement) 00287 { 00288 if (pAttr.isA (Tags::tag_buffer)) 00289 { 00290 Buffer * b = dynamic_cast<Buffer*>(pIn.getPreviousObject()); 00291 if (b) setBuffer(b); 00292 else throw LogicException("Incorrect object type during read operation"); 00293 } 00294 else if (pAttr.isA (Tags::tag_operation)) 00295 { 00296 Operation * o = dynamic_cast<Operation*>(pIn.getPreviousObject()); 00297 if (o) setOperation(o); 00298 else throw LogicException("Incorrect object type during read operation"); 00299 } 00300 else if (pAttr.isA(Tags::tag_quantity)) 00301 setQuantity(pElement.getDouble()); 00302 else if (pAttr.isA(Tags::tag_priority)) 00303 setPriority(pElement.getInt()); 00304 else if (pAttr.isA(Tags::tag_name)) 00305 setName(pElement.getString()); 00306 else if (pAttr.isA(Tags::tag_alternate)) 00307 setAlternate(pElement.getString()); 00308 else if (pAttr.isA(Tags::tag_search)) 00309 setSearch(pElement.getString()); 00310 else if (pAttr.isA(Tags::tag_action)) 00311 { 00312 delete static_cast<Action*>(pIn.getUserArea()); 00313 pIn.setUserArea( 00314 new Action(MetaClass::decodeAction(pElement.getString().c_str())) 00315 ); 00316 } 00317 else if (pAttr.isA(Tags::tag_effective_end)) 00318 setEffectiveEnd(pElement.getDate()); 00319 else if (pAttr.isA(Tags::tag_effective_start)) 00320 setEffectiveStart(pElement.getDate()); 00321 else if (pIn.isObjectEnd()) 00322 { 00323 // The flow data are now all read in. See if it makes sense now... 00324 Action a = pIn.getUserArea() ? 00325 *static_cast<Action*>(pIn.getUserArea()) : 00326 ADD_CHANGE; 00327 delete static_cast<Action*>(pIn.getUserArea()); 00328 try { validate(a); } 00329 catch (...) 00330 { 00331 delete this; 00332 throw; 00333 } 00334 } 00335 } 00336 00337 00338 DECLARE_EXPORT void FlowEnd::writeElement 00339 (XMLOutput *o, const Keyword& tag, mode m) const 00340 { 00341 // If the flow has already been saved, no need to repeat it again 00342 // A 'reference' to a flow is not useful to be saved. 00343 if (m == REFERENCE) return; 00344 assert(m != NOHEADER); 00345 00346 // Write the header 00347 o->BeginObject(tag, Tags::tag_type, getType().type); 00348 00349 // If the flow is defined inside of an operation tag, we don't need to save 00350 // the operation. Otherwise we do save it... 00351 if (!dynamic_cast<Operation*>(o->getPreviousObject())) 00352 o->writeElement(Tags::tag_operation, getOperation()); 00353 00354 // If the flow is defined inside of an buffer tag, we don't need to save 00355 // the buffer. Otherwise we do save it... 00356 if (!dynamic_cast<Buffer*>(o->getPreviousObject())) 00357 o->writeElement(Tags::tag_buffer, getBuffer()); 00358 00359 // Write the quantity, priority name and alternate 00360 o->writeElement(Tags::tag_quantity, getQuantity()); 00361 if (getPriority()!=1) o->writeElement(Tags::tag_priority, getPriority()); 00362 if (!getName().empty()) o->writeElement(Tags::tag_name, getName()); 00363 if (getAlternate()) 00364 o->writeElement(Tags::tag_alternate, getAlternate()->getName()); 00365 00366 // Write the effective daterange 00367 if (getEffective().getStart() != Date::infinitePast) 00368 o->writeElement(Tags::tag_effective_start, getEffective().getStart()); 00369 if (getEffective().getEnd() != Date::infiniteFuture) 00370 o->writeElement(Tags::tag_effective_end, getEffective().getEnd()); 00371 00372 // End of flow object 00373 o->EndObject(tag); 00374 } 00375 00376 00377 DECLARE_EXPORT PyObject* Flow::getattro(const Attribute& attr) 00378 { 00379 if (attr.isA(Tags::tag_buffer)) 00380 return PythonObject(getBuffer()); 00381 if (attr.isA(Tags::tag_operation)) 00382 return PythonObject(getOperation()); 00383 if (attr.isA(Tags::tag_quantity)) 00384 return PythonObject(getQuantity()); 00385 if (attr.isA(Tags::tag_priority)) 00386 return PythonObject(getPriority()); 00387 if (attr.isA(Tags::tag_effective_end)) 00388 return PythonObject(getEffective().getEnd()); 00389 if (attr.isA(Tags::tag_effective_start)) 00390 return PythonObject(getEffective().getStart()); 00391 if (attr.isA(Tags::tag_name)) 00392 return PythonObject(getName()); 00393 if (attr.isA(Tags::tag_hidden)) 00394 return PythonObject(getHidden()); 00395 if (attr.isA(Tags::tag_alternate)) 00396 return PythonObject(getAlternate()); 00397 if (attr.isA(Tags::tag_search)) 00398 { 00399 ostringstream ch; 00400 ch << getSearch(); 00401 return PythonObject(ch.str()); 00402 } 00403 return NULL; 00404 } 00405 00406 00407 DECLARE_EXPORT int Flow::setattro(const Attribute& attr, const PythonObject& field) 00408 { 00409 if (attr.isA(Tags::tag_buffer)) 00410 { 00411 if (!field.check(Buffer::metadata)) 00412 { 00413 PyErr_SetString(PythonDataException, "flow buffer must be of type buffer"); 00414 return -1; 00415 } 00416 Buffer* y = static_cast<Buffer*>(static_cast<PyObject*>(field)); 00417 setBuffer(y); 00418 } 00419 else if (attr.isA(Tags::tag_operation)) 00420 { 00421 if (!field.check(Operation::metadata)) 00422 { 00423 PyErr_SetString(PythonDataException, "flow operation must be of type operation"); 00424 return -1; 00425 } 00426 Operation* y = static_cast<Operation*>(static_cast<PyObject*>(field)); 00427 setOperation(y); 00428 } 00429 else if (attr.isA(Tags::tag_quantity)) 00430 setQuantity(field.getDouble()); 00431 else if (attr.isA(Tags::tag_priority)) 00432 setPriority(field.getInt()); 00433 else if (attr.isA(Tags::tag_effective_end)) 00434 setEffectiveEnd(field.getDate()); 00435 else if (attr.isA(Tags::tag_effective_start)) 00436 setEffectiveStart(field.getDate()); 00437 else if (attr.isA(Tags::tag_name)) 00438 setName(field.getString()); 00439 else if (attr.isA(Tags::tag_alternate)) 00440 { 00441 if (!field.check(Flow::metadata)) 00442 setAlternate(field.getString()); 00443 else 00444 { 00445 Flow *y = static_cast<Flow*>(static_cast<PyObject*>(field)); 00446 setAlternate(y); 00447 } 00448 } 00449 else if (attr.isA(Tags::tag_search)) 00450 setSearch(field.getString()); 00451 else 00452 return -1; 00453 return 0; 00454 } 00455 00456 00457 /** @todo method implementation not generic and doesn't support clean subclassing. */ 00458 PyObject* Flow::create(PyTypeObject* pytype, PyObject* args, PyObject* kwds) 00459 { 00460 try 00461 { 00462 // Pick up the operation 00463 PyObject* oper = PyDict_GetItemString(kwds,"operation"); 00464 if (!PyObject_TypeCheck(oper, Operation::metadata->pythonClass)) 00465 throw DataException("flow operation must be of type operation"); 00466 00467 // Pick up the resource 00468 PyObject* buf = PyDict_GetItemString(kwds,"buffer"); 00469 if (!PyObject_TypeCheck(buf, Buffer::metadata->pythonClass)) 00470 throw DataException("flow buffer must be of type buffer"); 00471 00472 // Pick up the quantity 00473 PyObject* q1 = PyDict_GetItemString(kwds,"quantity"); 00474 double q2 = q1 ? PythonObject(q1).getDouble() : 1.0; 00475 00476 // Pick up the effectivity dates 00477 DateRange eff; 00478 PyObject* eff_start = PyDict_GetItemString(kwds,"effective_start"); 00479 if (eff_start) 00480 { 00481 PythonObject d(eff_start); 00482 eff.setStart(d.getDate()); 00483 } 00484 PyObject* eff_end = PyDict_GetItemString(kwds,"effective_end"); 00485 if (eff_end) 00486 { 00487 PythonObject d(eff_end); 00488 eff.setEnd(d.getDate()); 00489 } 00490 00491 // Pick up the type and create the flow 00492 Flow *l; 00493 PyObject* t = PyDict_GetItemString(kwds,"type"); 00494 if (t) 00495 { 00496 PythonObject d(t); 00497 if (d.getString() == "flow_end") 00498 l = new FlowEnd( 00499 static_cast<Operation*>(oper), 00500 static_cast<Buffer*>(buf), 00501 q2, eff 00502 ); 00503 else 00504 l = new FlowStart( 00505 static_cast<Operation*>(oper), 00506 static_cast<Buffer*>(buf), 00507 q2, eff 00508 ); 00509 } 00510 else 00511 l = new FlowStart( 00512 static_cast<Operation*>(oper), 00513 static_cast<Buffer*>(buf), 00514 q2, eff 00515 ); 00516 00517 // Return the object 00518 Py_INCREF(l); 00519 return static_cast<PyObject*>(l); 00520 } 00521 catch (...) 00522 { 00523 PythonType::evalException(); 00524 return NULL; 00525 } 00526 } 00527 00528 00529 int FlowIterator::initialize() 00530 { 00531 // Initialize the type 00532 PythonType& x = PythonExtension<FlowIterator>::getType(); 00533 x.setName("flowIterator"); 00534 x.setDoc("frePPLe iterator for flows"); 00535 x.supportiter(); 00536 return x.typeReady(); 00537 } 00538 00539 00540 PyObject* FlowIterator::iternext() 00541 { 00542 PyObject* result; 00543 if (buf) 00544 { 00545 // Iterate over flows on a buffer 00546 if (ib == buf->getFlows().end()) return NULL; 00547 result = const_cast<Flow*>(&*ib); 00548 ++ib; 00549 } 00550 else 00551 { 00552 // Iterate over flows on an operation 00553 if (io == oper->getFlows().end()) return NULL; 00554 result = const_cast<Flow*>(&*io); 00555 ++io; 00556 } 00557 Py_INCREF(result); 00558 return result; 00559 } 00560 00561 } // end namespace