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