flow.cpp
Go to the documentation of this file.
00001 /*************************************************************************** 00002 file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/tags/0.9.1/src/model/flow.cpp $ 00003 version : $LastChangedRevision: 1656 $ $LastChangedBy: jdetaeye $ 00004 date : $LastChangedDate: 2012-03-27 19:05:34 +0200 (Tue, 27 Mar 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 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 if (!oper && !buf) 00089 throw DataException("Missing operation and buffer on a flow"); 00090 else if (!oper) 00091 throw DataException("Missing operation on a flow with buffer '" 00092 + buf->getName() + "'"); 00093 else 00094 throw DataException("Missing buffer on a flow with operation '" 00095 + oper->getName() + "'"); 00096 } 00097 00098 // Check if a flow with 1) identical buffer, 2) identical operation and 00099 // 3) overlapping effectivity dates already exists 00100 Operation::flowlist::const_iterator i = oper->getFlows().begin(); 00101 for (; i != oper->getFlows().end(); ++i) 00102 if (i->getBuffer() == buf 00103 && i->getEffective().overlap(getEffective()) 00104 && &*i != this) 00105 break; 00106 00107 // Apply the appropriate action 00108 switch (action) 00109 { 00110 case ADD: 00111 if (i != oper->getFlows().end()) 00112 throw DataException("Flow of '" + oper->getName() + "' and '" + 00113 buf->getName() + "' already exists"); 00114 break; 00115 case CHANGE: 00116 throw DataException("Can't update a flow"); 00117 case ADD_CHANGE: 00118 // ADD is handled in the code after the switch statement 00119 if (i == oper->getFlows().end()) break; 00120 throw DataException("Can't update a flow"); 00121 case REMOVE: 00122 // Delete the temporary flow object 00123 delete this; 00124 // Nothing to delete 00125 if (i == oper->getFlows().end()) 00126 throw DataException("Can't remove nonexistent flow of '" 00127 + oper->getName() + "' and '" + buf->getName() + "'"); 00128 // Delete 00129 delete &*i; 00130 } 00131 00132 // Attach to buffers higher up in the hierarchy 00133 // Note that the owner can create more loads if it has an owner too. 00134 if (buf->hasOwner() && action!=REMOVE) new Flow(oper, buf->getOwner(), quantity); 00135 00136 // Set a flag to make sure the level computation is triggered again 00137 HasLevel::triggerLazyRecomputation(); 00138 } 00139 00140 00141 DECLARE_EXPORT Flow::~Flow() 00142 { 00143 // Set a flag to make sure the level computation is triggered again 00144 HasLevel::triggerLazyRecomputation(); 00145 00146 // Delete existing flowplans 00147 if (getOperation() && getBuffer()) 00148 { 00149 // Loop over operationplans 00150 for(OperationPlan::iterator i(getOperation()); i != OperationPlan::end(); ++i) 00151 // Loop over flowplans 00152 for(OperationPlan::FlowPlanIterator j = i->beginFlowPlans(); j != i->endFlowPlans(); ) 00153 if (j->getFlow() == this) j.deleteFlowPlan(); 00154 else ++j; 00155 } 00156 00157 // Delete the flow from the operation and the buffer 00158 if (getOperation()) getOperation()->flowdata.erase(this); 00159 if (getBuffer()) getBuffer()->flows.erase(this); 00160 00161 // Clean up alternate flows 00162 if (hasAlts) 00163 { 00164 // The flow has alternates. 00165 // Make a new flow the leading one. Or if there is only one alternate 00166 // present it is not marked as an alternate any more. 00167 unsigned short cnt = 0; 00168 int minprio = INT_MAX; 00169 Flow* newLeader = NULL; 00170 for (Operation::flowlist::iterator i = getOperation()->flowdata.begin(); 00171 i != getOperation()->flowdata.end(); ++i) 00172 if (i->altFlow == this) 00173 { 00174 cnt++; 00175 if (i->priority < minprio) 00176 { 00177 newLeader = &*i; 00178 minprio = i->priority; 00179 } 00180 } 00181 if (cnt < 1) 00182 throw LogicException("Alternate flows update failure"); 00183 else if (cnt == 1) 00184 // No longer an alternate any more 00185 newLeader->altFlow = NULL; 00186 else 00187 { 00188 // Mark a new leader flow 00189 newLeader->hasAlts = true; 00190 newLeader->altFlow = NULL; 00191 for (Operation::flowlist::iterator i = getOperation()->flowdata.begin(); 00192 i != getOperation()->flowdata.end(); ++i) 00193 if (i->altFlow == this) i->altFlow = newLeader; 00194 } 00195 } 00196 if (altFlow) 00197 { 00198 // The flow is an alternate of another one. 00199 // If it was the only alternate, then the hasAlts flag on the parent 00200 // flow needs to be set back to false 00201 bool only_one = true; 00202 for (Operation::flowlist::iterator i = getOperation()->flowdata.begin(); 00203 i != getOperation()->flowdata.end(); ++i) 00204 if (i->altFlow == altFlow) 00205 { 00206 only_one = false; 00207 break; 00208 } 00209 if (only_one) altFlow->hasAlts = false; 00210 } 00211 } 00212 00213 00214 DECLARE_EXPORT void Flow::setAlternate(Flow *f) 00215 { 00216 // Validate the argument 00217 if (!f) 00218 throw DataException("Setting NULL alternate flow"); 00219 if (hasAlts || f->altFlow) 00220 throw DataException("Nested alternate flows are not allowed"); 00221 if (!f->isConsumer() || !isConsumer()) 00222 throw DataException("Only consuming alternate flows are supported"); 00223 00224 // Update both flows 00225 f->hasAlts = true; 00226 altFlow = f; 00227 } 00228 00229 00230 DECLARE_EXPORT void Flow::setAlternate(const string& n) 00231 { 00232 if (!getOperation()) 00233 throw LogicException("Can't set an alternate flow before setting the operation"); 00234 Flow *x = getOperation()->flowdata.find(n); 00235 if (!x) throw DataException("Can't find flow with name '" + n + "'"); 00236 setAlternate(x); 00237 } 00238 00239 00240 DECLARE_EXPORT void Flow::writeElement (XMLOutput *o, const Keyword& tag, mode m) const 00241 { 00242 // If the flow has already been saved, no need to repeat it again 00243 // A 'reference' to a flow is not useful to be saved. 00244 if (m == REFERENCE) return; 00245 assert(m != NOHEADER); 00246 00247 // Write the header 00248 o->BeginObject(tag, Tags::tag_type, getType().type); 00249 00250 // If the flow is defined inside of an operation tag, we don't need to save 00251 // the operation. Otherwise we do save it... 00252 if (!dynamic_cast<Operation*>(o->getPreviousObject())) 00253 o->writeElement(Tags::tag_operation, getOperation()); 00254 00255 // If the flow is defined inside of an buffer tag, we don't need to save 00256 // the buffer. Otherwise we do save it... 00257 if (!dynamic_cast<Buffer*>(o->getPreviousObject())) 00258 o->writeElement(Tags::tag_buffer, getBuffer()); 00259 00260 // Write the quantity, priority, name and alternate 00261 o->writeElement(Tags::tag_quantity, getQuantity()); 00262 if (getPriority()!=1) o->writeElement(Tags::tag_priority, getPriority()); 00263 if (!getName().empty()) o->writeElement(Tags::tag_name, getName()); 00264 if (getAlternate()) 00265 o->writeElement(Tags::tag_alternate, getAlternate()->getName()); 00266 00267 // Write the effective daterange 00268 if (getEffective().getStart() != Date::infinitePast) 00269 o->writeElement(Tags::tag_effective_start, getEffective().getStart()); 00270 if (getEffective().getEnd() != Date::infiniteFuture) 00271 o->writeElement(Tags::tag_effective_end, getEffective().getEnd()); 00272 00273 // End of flow object 00274 o->EndObject(tag); 00275 } 00276 00277 00278 DECLARE_EXPORT void Flow::beginElement(XMLInput& pIn, const Attribute& pAttr) 00279 { 00280 if (pAttr.isA (Tags::tag_buffer)) 00281 pIn.readto( Buffer::reader(Buffer::metadata,pIn.getAttributes()) ); 00282 else if (pAttr.isA (Tags::tag_operation)) 00283 pIn.readto( Operation::reader(Operation::metadata,pIn.getAttributes()) ); 00284 } 00285 00286 00287 DECLARE_EXPORT void Flow::endElement (XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement) 00288 { 00289 if (pAttr.isA (Tags::tag_buffer)) 00290 { 00291 Buffer * b = dynamic_cast<Buffer*>(pIn.getPreviousObject()); 00292 if (b) setBuffer(b); 00293 else throw LogicException("Incorrect object type during read operation"); 00294 } 00295 else if (pAttr.isA (Tags::tag_operation)) 00296 { 00297 Operation * o = dynamic_cast<Operation*>(pIn.getPreviousObject()); 00298 if (o) setOperation(o); 00299 else throw LogicException("Incorrect object type during read operation"); 00300 } 00301 else if (pAttr.isA(Tags::tag_quantity)) 00302 setQuantity(pElement.getDouble()); 00303 else if (pAttr.isA(Tags::tag_priority)) 00304 setPriority(pElement.getInt()); 00305 else if (pAttr.isA(Tags::tag_name)) 00306 setName(pElement.getString()); 00307 else if (pAttr.isA(Tags::tag_alternate)) 00308 setAlternate(pElement.getString()); 00309 else if (pAttr.isA(Tags::tag_search)) 00310 setSearch(pElement.getString()); 00311 else if (pAttr.isA(Tags::tag_action)) 00312 { 00313 delete static_cast<Action*>(pIn.getUserArea()); 00314 pIn.setUserArea( 00315 new Action(MetaClass::decodeAction(pElement.getString().c_str())) 00316 ); 00317 } 00318 else if (pAttr.isA(Tags::tag_effective_end)) 00319 setEffectiveEnd(pElement.getDate()); 00320 else if (pAttr.isA(Tags::tag_effective_start)) 00321 setEffectiveStart(pElement.getDate()); 00322 else if (pIn.isObjectEnd()) 00323 { 00324 // The flow data are now all read in. See if it makes sense now... 00325 Action a = pIn.getUserArea() ? 00326 *static_cast<Action*>(pIn.getUserArea()) : 00327 ADD_CHANGE; 00328 delete static_cast<Action*>(pIn.getUserArea()); 00329 try { validate(a); } 00330 catch (...) 00331 { 00332 delete this; 00333 throw; 00334 } 00335 } 00336 } 00337 00338 00339 DECLARE_EXPORT void FlowEnd::writeElement 00340 (XMLOutput *o, const Keyword& tag, mode m) const 00341 { 00342 // If the flow has already been saved, no need to repeat it again 00343 // A 'reference' to a flow is not useful to be saved. 00344 if (m == REFERENCE) return; 00345 assert(m != NOHEADER); 00346 00347 // Write the header 00348 o->BeginObject(tag, Tags::tag_type, getType().type); 00349 00350 // If the flow is defined inside of an operation tag, we don't need to save 00351 // the operation. Otherwise we do save it... 00352 if (!dynamic_cast<Operation*>(o->getPreviousObject())) 00353 o->writeElement(Tags::tag_operation, getOperation()); 00354 00355 // If the flow is defined inside of an buffer tag, we don't need to save 00356 // the buffer. Otherwise we do save it... 00357 if (!dynamic_cast<Buffer*>(o->getPreviousObject())) 00358 o->writeElement(Tags::tag_buffer, getBuffer()); 00359 00360 // Write the quantity, priority name and alternate 00361 o->writeElement(Tags::tag_quantity, getQuantity()); 00362 if (getPriority()!=1) o->writeElement(Tags::tag_priority, getPriority()); 00363 if (!getName().empty()) o->writeElement(Tags::tag_name, getName()); 00364 if (getAlternate()) 00365 o->writeElement(Tags::tag_alternate, getAlternate()->getName()); 00366 00367 // Write the effective daterange 00368 if (getEffective().getStart() != Date::infinitePast) 00369 o->writeElement(Tags::tag_effective_start, getEffective().getStart()); 00370 if (getEffective().getEnd() != Date::infiniteFuture) 00371 o->writeElement(Tags::tag_effective_end, getEffective().getEnd()); 00372 00373 // End of flow object 00374 o->EndObject(tag); 00375 } 00376 00377 00378 DECLARE_EXPORT PyObject* Flow::getattro(const Attribute& attr) 00379 { 00380 if (attr.isA(Tags::tag_buffer)) 00381 return PythonObject(getBuffer()); 00382 if (attr.isA(Tags::tag_operation)) 00383 return PythonObject(getOperation()); 00384 if (attr.isA(Tags::tag_quantity)) 00385 return PythonObject(getQuantity()); 00386 if (attr.isA(Tags::tag_priority)) 00387 return PythonObject(getPriority()); 00388 if (attr.isA(Tags::tag_effective_end)) 00389 return PythonObject(getEffective().getEnd()); 00390 if (attr.isA(Tags::tag_effective_start)) 00391 return PythonObject(getEffective().getStart()); 00392 if (attr.isA(Tags::tag_name)) 00393 return PythonObject(getName()); 00394 if (attr.isA(Tags::tag_hidden)) 00395 return PythonObject(getHidden()); 00396 if (attr.isA(Tags::tag_alternate)) 00397 return PythonObject(getAlternate()); 00398 if (attr.isA(Tags::tag_search)) 00399 { 00400 ostringstream ch; 00401 ch << getSearch(); 00402 return PythonObject(ch.str()); 00403 } 00404 return NULL; 00405 } 00406 00407 00408 DECLARE_EXPORT int Flow::setattro(const Attribute& attr, const PythonObject& field) 00409 { 00410 if (attr.isA(Tags::tag_buffer)) 00411 { 00412 if (!field.check(Buffer::metadata)) 00413 { 00414 PyErr_SetString(PythonDataException, "flow buffer must be of type buffer"); 00415 return -1; 00416 } 00417 Buffer* y = static_cast<Buffer*>(static_cast<PyObject*>(field)); 00418 setBuffer(y); 00419 } 00420 else if (attr.isA(Tags::tag_operation)) 00421 { 00422 if (!field.check(Operation::metadata)) 00423 { 00424 PyErr_SetString(PythonDataException, "flow operation must be of type operation"); 00425 return -1; 00426 } 00427 Operation* y = static_cast<Operation*>(static_cast<PyObject*>(field)); 00428 setOperation(y); 00429 } 00430 else if (attr.isA(Tags::tag_quantity)) 00431 setQuantity(field.getDouble()); 00432 else if (attr.isA(Tags::tag_priority)) 00433 setPriority(field.getInt()); 00434 else if (attr.isA(Tags::tag_effective_end)) 00435 setEffectiveEnd(field.getDate()); 00436 else if (attr.isA(Tags::tag_effective_start)) 00437 setEffectiveStart(field.getDate()); 00438 else if (attr.isA(Tags::tag_name)) 00439 setName(field.getString()); 00440 else if (attr.isA(Tags::tag_alternate)) 00441 { 00442 if (!field.check(Flow::metadata)) 00443 setAlternate(field.getString()); 00444 else 00445 { 00446 Flow *y = static_cast<Flow*>(static_cast<PyObject*>(field)); 00447 setAlternate(y); 00448 } 00449 } 00450 else if (attr.isA(Tags::tag_search)) 00451 setSearch(field.getString()); 00452 else 00453 return -1; 00454 return 0; 00455 } 00456 00457 00458 /** @todo method implementation not generic and doesn't support clean subclassing. */ 00459 PyObject* Flow::create(PyTypeObject* pytype, PyObject* args, PyObject* kwds) 00460 { 00461 try 00462 { 00463 // Pick up the operation 00464 PyObject* oper = PyDict_GetItemString(kwds,"operation"); 00465 if (!PyObject_TypeCheck(oper, Operation::metadata->pythonClass)) 00466 throw DataException("flow operation must be of type operation"); 00467 00468 // Pick up the resource 00469 PyObject* buf = PyDict_GetItemString(kwds,"buffer"); 00470 if (!PyObject_TypeCheck(buf, Buffer::metadata->pythonClass)) 00471 throw DataException("flow buffer must be of type buffer"); 00472 00473 // Pick up the quantity 00474 PyObject* q1 = PyDict_GetItemString(kwds,"quantity"); 00475 double q2 = q1 ? PythonObject(q1).getDouble() : 1.0; 00476 00477 // Pick up the effectivity dates 00478 DateRange eff; 00479 PyObject* eff_start = PyDict_GetItemString(kwds,"effective_start"); 00480 if (eff_start) 00481 { 00482 PythonObject d(eff_start); 00483 eff.setStart(d.getDate()); 00484 } 00485 PyObject* eff_end = PyDict_GetItemString(kwds,"effective_end"); 00486 if (eff_end) 00487 { 00488 PythonObject d(eff_end); 00489 eff.setEnd(d.getDate()); 00490 } 00491 00492 // Pick up the type and create the flow 00493 Flow *l; 00494 PyObject* t = PyDict_GetItemString(kwds,"type"); 00495 if (t) 00496 { 00497 PythonObject d(t); 00498 if (d.getString() == "flow_end") 00499 l = new FlowEnd( 00500 static_cast<Operation*>(oper), 00501 static_cast<Buffer*>(buf), 00502 q2, eff 00503 ); 00504 else 00505 l = new FlowStart( 00506 static_cast<Operation*>(oper), 00507 static_cast<Buffer*>(buf), 00508 q2, eff 00509 ); 00510 } 00511 else 00512 l = new FlowStart( 00513 static_cast<Operation*>(oper), 00514 static_cast<Buffer*>(buf), 00515 q2, eff 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