load.cpp
Go to the documentation of this file.
00001 /*************************************************************************** 00002 file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/trunk/src/model/load.cpp $ 00003 version : $LastChangedRevision: 1505 $ $LastChangedBy: jdetaeye $ 00004 date : $LastChangedDate: 2011-08-26 18:55:08 +0200 (Fri, 26 Aug 2011) $ 00005 ***************************************************************************/ 00006 00007 /*************************************************************************** 00008 * * 00009 * Copyright (C) 2007-2011 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* Load::metadata; 00034 00035 00036 int Load::initialize() 00037 { 00038 // Initialize the metadata 00039 metadata = new MetaCategory 00040 ("load", "loads", MetaCategory::ControllerDefault, writer); 00041 const_cast<MetaCategory*>(metadata)->registerClass( 00042 "load","load",true,Object::createDefault<Load> 00043 ); 00044 00045 // Initialize the Python class 00046 PythonType& x = FreppleCategory<Load>::getType(); 00047 x.setName("load"); 00048 x.setDoc("frePPLe load"); 00049 x.supportgetattro(); 00050 x.supportsetattro(); 00051 x.supportcreate(create); 00052 x.addMethod("toXML", toXML, METH_VARARGS, "return a XML representation"); 00053 const_cast<MetaCategory*>(Load::metadata)->pythonClass = x.type_object(); 00054 return x.typeReady(); 00055 } 00056 00057 00058 void Load::writer(const MetaCategory* c, XMLOutput* o) 00059 { 00060 bool firstload = true; 00061 for (Operation::iterator i = Operation::begin(); i != Operation::end(); ++i) 00062 for (Operation::loadlist::const_iterator j = i->getLoads().begin(); j != i->getLoads().end(); ++j) 00063 { 00064 if (firstload) 00065 { 00066 o->BeginObject(Tags::tag_loads); 00067 firstload = false; 00068 } 00069 // We use the FULL mode, to force the loads being written regardless 00070 // of the depth in the XML tree. 00071 o->writeElement(Tags::tag_load, &*j, FULL); 00072 } 00073 if (!firstload) o->EndObject(Tags::tag_loads); 00074 } 00075 00076 00077 DECLARE_EXPORT void Load::validate(Action action) 00078 { 00079 // Catch null operation and resource pointers 00080 Operation *oper = getOperation(); 00081 Resource *res = getResource(); 00082 if (!oper || !res) 00083 { 00084 // Invalid load model 00085 if (!oper && !res) 00086 throw DataException("Missing operation and resource on a load"); 00087 else if (!oper) 00088 throw DataException("Missing operation on a load on resource '" 00089 + res->getName() + "'"); 00090 else if (!res) 00091 throw DataException("Missing resource on a load on operation '" 00092 + oper->getName() + "'"); 00093 } 00094 00095 // Check if a load with 1) identical resource, 2) identical operation and 00096 // 3) overlapping effectivity dates already exists 00097 Operation::loadlist::const_iterator i = oper->getLoads().begin(); 00098 for (;i != oper->getLoads().end(); ++i) 00099 if (i->getResource() == res 00100 && i->getEffective().overlap(getEffective()) 00101 && &*i != this) 00102 break; 00103 00104 // Apply the appropriate action 00105 switch (action) 00106 { 00107 case ADD: 00108 if (i != oper->getLoads().end()) 00109 { 00110 throw DataException("Load of '" + oper->getName() + "' and '" 00111 + res->getName() + "' already exists"); 00112 } 00113 break; 00114 case CHANGE: 00115 throw DataException("Can't update a load"); 00116 case ADD_CHANGE: 00117 // ADD is handled in the code after the switch statement 00118 if (i == oper->getLoads().end()) break; 00119 throw DataException("Can't update a load"); 00120 case REMOVE: 00121 // This load was only used temporarily during the reading process 00122 delete this; 00123 if (i == oper->getLoads().end()) 00124 // Nothing to delete 00125 throw DataException("Can't remove nonexistent load of '" 00126 + oper->getName() + "' and '" + res->getName() + "'"); 00127 delete &*i; 00128 // Set a flag to make sure the level computation is triggered again 00129 HasLevel::triggerLazyRecomputation(); 00130 return; 00131 } 00132 00133 // The statements below should be executed only when a new load is created. 00134 00135 // If the resource has an owner, also load the owner 00136 // Note that the owner load can create more loads if it has an owner too. 00137 if (res->hasOwner() && action!=REMOVE) new Load(oper, res->getOwner(), qty); 00138 00139 // Set a flag to make sure the level computation is triggered again 00140 HasLevel::triggerLazyRecomputation(); 00141 } 00142 00143 00144 DECLARE_EXPORT Load::~Load() 00145 { 00146 // Set a flag to make sure the level computation is triggered again 00147 HasLevel::triggerLazyRecomputation(); 00148 00149 // Delete existing loadplans 00150 if (getOperation() && getResource()) 00151 { 00152 // Loop over operationplans 00153 for(OperationPlan::iterator i(getOperation()); i != OperationPlan::end(); ++i) 00154 // Loop over loadplans 00155 for(OperationPlan::LoadPlanIterator j = i->beginLoadPlans(); j != i->endLoadPlans(); ) 00156 if (j->getLoad() == this) j.deleteLoadPlan(); 00157 else ++j; 00158 } 00159 00160 // Delete the load from the operation and resource 00161 if (getOperation()) getOperation()->loaddata.erase(this); 00162 if (getResource()) getResource()->loads.erase(this); 00163 00164 // Clean up alternate loads 00165 if (hasAlts) 00166 { 00167 // The load has alternates. 00168 // Make a new load the leading one. Or if there is only one alternate 00169 // present it is not marked as an alternate any more. 00170 unsigned short cnt = 0; 00171 int minprio = INT_MAX; 00172 Load* newLeader = NULL; 00173 for (Operation::loadlist::iterator i = getOperation()->loaddata.begin(); 00174 i != getOperation()->loaddata.end(); ++i) 00175 if (i->altLoad == this) 00176 { 00177 cnt++; 00178 if (i->priority < minprio) 00179 { 00180 newLeader = &*i; 00181 minprio = i->priority; 00182 } 00183 } 00184 if (cnt < 1) 00185 throw LogicException("Alternate loads update failure"); 00186 else if (cnt == 1) 00187 // No longer an alternate any more 00188 newLeader->altLoad = NULL; 00189 else 00190 { 00191 // Mark a new leader load 00192 newLeader->hasAlts = true; 00193 newLeader->altLoad = NULL; 00194 for (Operation::loadlist::iterator i = getOperation()->loaddata.begin(); 00195 i != getOperation()->loaddata.end(); ++i) 00196 if (i->altLoad == this) i->altLoad = newLeader; 00197 } 00198 } 00199 if (altLoad) 00200 { 00201 // The load is an alternate of another one. 00202 // If it was the only alternate, then the hasAlts flag on the parent 00203 // load needs to be set back to false 00204 bool only_one = true; 00205 for (Operation::loadlist::iterator i = getOperation()->loaddata.begin(); 00206 i != getOperation()->loaddata.end(); ++i) 00207 if (i->altLoad == altLoad) 00208 { 00209 only_one = false; 00210 break; 00211 } 00212 if (only_one) altLoad->hasAlts = false; 00213 } 00214 } 00215 00216 00217 DECLARE_EXPORT void Load::setAlternate(Load *f) 00218 { 00219 // Validate the argument 00220 if (!f) 00221 throw DataException("Setting NULL alternate load"); 00222 if (hasAlts || f->altLoad) 00223 throw DataException("Nested alternate loads are not allowed"); 00224 00225 // Update both flows 00226 f->hasAlts = true; 00227 altLoad = f; 00228 } 00229 00230 00231 DECLARE_EXPORT void Load::setAlternate(const string& n) 00232 { 00233 if (!getOperation()) 00234 throw LogicException("Can't set an alternate load before setting the operation"); 00235 Load *x = getOperation()->loaddata.find(n); 00236 if (!x) throw DataException("Can't find load with name '" + n + "'"); 00237 setAlternate(x); 00238 } 00239 00240 00241 DECLARE_EXPORT void Load::setSetup(const string n) 00242 { 00243 setup = n; 00244 00245 if (!setup.empty()) 00246 { 00247 // Guarantuee that only a single load has a setup. 00248 // Alternates of that load can have a setup as well. 00249 for (Operation::loadlist::iterator i = getOperation()->loaddata.begin(); 00250 i != getOperation()->loaddata.end(); ++i) 00251 if (&*i != this && !i->setup.empty() 00252 && i->getAlternate() != this && getAlternate() != &*i 00253 && i->getAlternate() != getAlternate()) 00254 throw DataException("Only a single load of an operation can specify a setup"); 00255 } 00256 } 00257 00258 00259 DECLARE_EXPORT void Load::writeElement(XMLOutput *o, const Keyword& tag, mode m) const 00260 { 00261 // If the load has already been saved, no need to repeat it again 00262 // A 'reference' to a load is not useful to be saved. 00263 if (m == REFERENCE) return; 00264 assert(m != NOHEADER); 00265 00266 o->BeginObject(tag); 00267 00268 // If the load is defined inside of an operation tag, we don't need to save 00269 // the operation. Otherwise we do save it... 00270 if (!dynamic_cast<Operation*>(o->getPreviousObject())) 00271 o->writeElement(Tags::tag_operation, getOperation()); 00272 00273 // If the load is defined inside of an resource tag, we don't need to save 00274 // the resource. Otherwise we do save it... 00275 if (!dynamic_cast<Resource*>(o->getPreviousObject())) 00276 o->writeElement(Tags::tag_resource, getResource()); 00277 00278 // Write the quantity, priority, name and alternate 00279 if (qty != 1.0) o->writeElement(Tags::tag_quantity, qty); 00280 if (getPriority()!=1) o->writeElement(Tags::tag_priority, getPriority()); 00281 if (!getName().empty()) o->writeElement(Tags::tag_name, getName()); 00282 if (getAlternate()) 00283 o->writeElement(Tags::tag_alternate, getAlternate()->getName()); 00284 if (search != PRIORITY) 00285 { 00286 ostringstream ch; 00287 ch << getSearch(); 00288 o->writeElement(Tags::tag_search, ch.str()); 00289 } 00290 00291 // Write the effective daterange 00292 if (getEffective().getStart() != Date::infinitePast) 00293 o->writeElement(Tags::tag_effective_start, getEffective().getStart()); 00294 if (getEffective().getEnd() != Date::infiniteFuture) 00295 o->writeElement(Tags::tag_effective_end, getEffective().getEnd()); 00296 00297 // Write the required setup 00298 if (!setup.empty()) o->writeElement(Tags::tag_setup, setup); 00299 00300 o->EndObject(tag); 00301 } 00302 00303 00304 DECLARE_EXPORT void Load::beginElement(XMLInput& pIn, const Attribute& pAttr) 00305 { 00306 if (pAttr.isA (Tags::tag_resource)) 00307 pIn.readto( Resource::reader(Resource::metadata,pIn.getAttributes()) ); 00308 else if (pAttr.isA (Tags::tag_operation)) 00309 pIn.readto( Operation::reader(Operation::metadata,pIn.getAttributes()) ); 00310 } 00311 00312 00313 DECLARE_EXPORT void Load::endElement (XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement) 00314 { 00315 if (pAttr.isA (Tags::tag_resource)) 00316 { 00317 Resource * r = dynamic_cast<Resource*>(pIn.getPreviousObject()); 00318 if (r) setResource(r); 00319 else throw LogicException("Incorrect object type during read operation"); 00320 } 00321 else if (pAttr.isA (Tags::tag_operation)) 00322 { 00323 Operation * o = dynamic_cast<Operation*>(pIn.getPreviousObject()); 00324 if (o) setOperation(o); 00325 else throw LogicException("Incorrect object type during read operation"); 00326 } 00327 else if (pAttr.isA(Tags::tag_quantity)) 00328 setQuantity(pElement.getDouble()); 00329 else if (pAttr.isA(Tags::tag_priority)) 00330 setPriority(pElement.getInt()); 00331 else if (pAttr.isA(Tags::tag_name)) 00332 setName(pElement.getString()); 00333 else if (pAttr.isA(Tags::tag_alternate)) 00334 setAlternate(pElement.getString()); 00335 else if (pAttr.isA(Tags::tag_search)) 00336 setSearch(pElement.getString()); 00337 else if (pAttr.isA(Tags::tag_setup)) 00338 setSetup(pElement.getString()); 00339 else if (pAttr.isA(Tags::tag_action)) 00340 { 00341 delete static_cast<Action*>(pIn.getUserArea()); 00342 pIn.setUserArea( 00343 new Action(MetaClass::decodeAction(pElement.getString().c_str())) 00344 ); 00345 } 00346 else if (pAttr.isA(Tags::tag_effective_end)) 00347 setEffectiveEnd(pElement.getDate()); 00348 else if (pAttr.isA(Tags::tag_effective_start)) 00349 setEffectiveStart(pElement.getDate()); 00350 else if (pIn.isObjectEnd()) 00351 { 00352 // The load data is now all read in. See if it makes sense now... 00353 try { 00354 validate(!pIn.getUserArea() ? 00355 ADD_CHANGE : 00356 *static_cast<Action*>(pIn.getUserArea()) 00357 ); 00358 } 00359 catch (...) { 00360 delete this; 00361 throw; 00362 } 00363 delete static_cast<Action*>(pIn.getUserArea()); 00364 } 00365 } 00366 00367 00368 DECLARE_EXPORT PyObject* Load::getattro(const Attribute& attr) 00369 { 00370 if (attr.isA(Tags::tag_resource)) 00371 return PythonObject(getResource()); 00372 if (attr.isA(Tags::tag_operation)) 00373 return PythonObject(getOperation()); 00374 if (attr.isA(Tags::tag_quantity)) 00375 return PythonObject(getQuantity()); 00376 if (attr.isA(Tags::tag_priority)) 00377 return PythonObject(getPriority()); 00378 if (attr.isA(Tags::tag_effective_end)) 00379 return PythonObject(getEffective().getEnd()); 00380 if (attr.isA(Tags::tag_effective_start)) 00381 return PythonObject(getEffective().getStart()); 00382 if (attr.isA(Tags::tag_name)) 00383 return PythonObject(getName()); 00384 if (attr.isA(Tags::tag_alternate)) 00385 return PythonObject(getAlternate()); 00386 if (attr.isA(Tags::tag_search)) 00387 { 00388 ostringstream ch; 00389 ch << getSearch(); 00390 return PythonObject(ch.str()); 00391 } 00392 if (attr.isA(Tags::tag_setup)) 00393 return PythonObject(getSetup()); 00394 return NULL; 00395 } 00396 00397 00398 DECLARE_EXPORT int Load::setattro(const Attribute& attr, const PythonObject& field) 00399 { 00400 if (attr.isA(Tags::tag_resource)) 00401 { 00402 if (!field.check(Resource::metadata)) 00403 { 00404 PyErr_SetString(PythonDataException, "load resource must be of type resource"); 00405 return -1; 00406 } 00407 Resource* y = static_cast<Resource*>(static_cast<PyObject*>(field)); 00408 setResource(y); 00409 } 00410 else if (attr.isA(Tags::tag_operation)) 00411 { 00412 if (!field.check(Operation::metadata)) 00413 { 00414 PyErr_SetString(PythonDataException, "load operation must be of type operation"); 00415 return -1; 00416 } 00417 Operation* y = static_cast<Operation*>(static_cast<PyObject*>(field)); 00418 setOperation(y); 00419 } 00420 else if (attr.isA(Tags::tag_quantity)) 00421 setQuantity(field.getDouble()); 00422 else if (attr.isA(Tags::tag_priority)) 00423 setPriority(field.getInt()); 00424 else if (attr.isA(Tags::tag_effective_end)) 00425 setEffectiveEnd(field.getDate()); 00426 else if (attr.isA(Tags::tag_effective_start)) 00427 setEffectiveStart(field.getDate()); 00428 else if (attr.isA(Tags::tag_name)) 00429 setName(field.getString()); 00430 else if (attr.isA(Tags::tag_alternate)) 00431 { 00432 if (!field.check(Load::metadata)) 00433 setAlternate(field.getString()); 00434 else 00435 { 00436 Load *y = static_cast<Load*>(static_cast<PyObject*>(field)); 00437 setAlternate(y); 00438 } 00439 } 00440 else if (attr.isA(Tags::tag_search)) 00441 setSearch(field.getString()); 00442 else if (attr.isA(Tags::tag_setup)) 00443 setSetup(field.getString()); 00444 else 00445 return -1; 00446 return 0; 00447 } 00448 00449 00450 /** @todo this method implementation is not generic enough and not extendible by subclasses. */ 00451 PyObject* Load::create(PyTypeObject* pytype, PyObject* args, PyObject* kwds) 00452 { 00453 try 00454 { 00455 // Pick up the operation 00456 PyObject* oper = PyDict_GetItemString(kwds,"operation"); 00457 if (!PyObject_TypeCheck(oper, Operation::metadata->pythonClass)) 00458 throw DataException("load operation must be of type operation"); 00459 00460 // Pick up the resource 00461 PyObject* res = PyDict_GetItemString(kwds,"resource"); 00462 if (!PyObject_TypeCheck(res, Resource::metadata->pythonClass)) 00463 throw DataException("load resource must be of type resource"); 00464 00465 // Pick up the quantity 00466 PyObject* q1 = PyDict_GetItemString(kwds,"quantity"); 00467 double q2 = q1 ? PythonObject(q1).getDouble() : 1.0; 00468 00469 // Pick up the effective dates 00470 DateRange eff; 00471 PyObject* eff_start = PyDict_GetItemString(kwds,"effective_start"); 00472 if (eff_start) 00473 { 00474 PythonObject d(eff_start); 00475 eff.setStart(d.getDate()); 00476 } 00477 PyObject* eff_end = PyDict_GetItemString(kwds,"effective_end"); 00478 if (eff_end) 00479 { 00480 PythonObject d(eff_end); 00481 eff.setEnd(d.getDate()); 00482 } 00483 00484 // Create the load 00485 Load *l = new Load( 00486 static_cast<Operation*>(oper), 00487 static_cast<Resource*>(res), 00488 q2, eff 00489 ); 00490 00491 // Return the object 00492 Py_INCREF(l); 00493 return static_cast<PyObject*>(l); 00494 } 00495 catch (...) 00496 { 00497 PythonType::evalException(); 00498 return NULL; 00499 } 00500 } 00501 00502 00503 int LoadIterator::initialize() 00504 { 00505 // Initialize the type 00506 PythonType& x = PythonExtension<LoadIterator>::getType(); 00507 x.setName("loadIterator"); 00508 x.setDoc("frePPLe iterator for loads"); 00509 x.supportiter(); 00510 return x.typeReady(); 00511 } 00512 00513 00514 PyObject* LoadIterator::iternext() 00515 { 00516 PyObject* result; 00517 if (res) 00518 { 00519 // Iterate over loads on a resource 00520 if (ir == res->getLoads().end()) return NULL; 00521 result = const_cast<Load*>(&*ir); 00522 ++ir; 00523 } 00524 else 00525 { 00526 // Iterate over loads on an operation 00527 if (io == oper->getLoads().end()) return NULL; 00528 result = const_cast<Load*>(&*io); 00529 ++io; 00530 } 00531 Py_INCREF(result); 00532 return result; 00533 } 00534 00535 } // end namespace