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