model/actions.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/actions.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 00031 namespace frepple 00032 { 00033 00034 // 00035 // READ XML INPUT FILE 00036 // 00037 00038 00039 DECLARE_EXPORT PyObject* readXMLfile(PyObject* self, PyObject* args) 00040 { 00041 // Pick up arguments 00042 char *filename = NULL; 00043 int validate(1), validate_only(0); 00044 int ok = PyArg_ParseTuple(args, "|sii:readXMLfile", &filename, &validate, &validate_only); 00045 if (!ok) return NULL; 00046 00047 // Execute and catch exceptions 00048 Py_BEGIN_ALLOW_THREADS // Free Python interpreter for other threads 00049 try 00050 { 00051 if (!filename) 00052 { 00053 // Read from standard input 00054 xercesc::StdInInputSource in; 00055 if (validate_only!=0) 00056 // When no root object is passed, only the input validation happens 00057 XMLInput().parse(in, NULL, true); 00058 else 00059 XMLInput().parse(in, &Plan::instance(), validate!=0); 00060 } 00061 else if (validate_only!=0) 00062 // Read and validate a file 00063 XMLInputFile(filename).parse(NULL, true); 00064 else 00065 // Read, execute and optionally validate a file 00066 XMLInputFile(filename).parse(&Plan::instance(),validate!=0); 00067 } 00068 catch (...) 00069 { 00070 Py_BLOCK_THREADS; 00071 PythonType::evalException(); 00072 return NULL; 00073 } 00074 Py_END_ALLOW_THREADS // Reclaim Python interpreter 00075 return Py_BuildValue(""); 00076 } 00077 00078 00079 // 00080 // READ XML INPUT STRING 00081 // 00082 00083 00084 DECLARE_EXPORT PyObject* readXMLdata(PyObject *self, PyObject *args) 00085 { 00086 // Pick up arguments 00087 char *data; 00088 int validate(1), validate_only(0); 00089 int ok = PyArg_ParseTuple(args, "s|ii:readXMLdata", &data, &validate, &validate_only); 00090 if (!ok) return NULL; 00091 00092 // Free Python interpreter for other threads 00093 Py_BEGIN_ALLOW_THREADS 00094 00095 // Execute and catch exceptions 00096 try 00097 { 00098 if (!data) 00099 throw DataException("No input data"); 00100 else if (validate_only!=0) 00101 XMLInputString(data).parse(NULL, true); 00102 else 00103 XMLInputString(data).parse(&Plan::instance(), validate!=0); 00104 } 00105 catch (...) 00106 { 00107 Py_BLOCK_THREADS; 00108 PythonType::evalException(); 00109 return NULL; 00110 } 00111 Py_END_ALLOW_THREADS // Reclaim Python interpreter 00112 return Py_BuildValue(""); // Safer than using Py_None, which is not portable across compilers 00113 } 00114 00115 00116 // 00117 // SAVE MODEL TO XML 00118 // 00119 00120 00121 PyObject* saveXMLfile(PyObject* self, PyObject* args) 00122 { 00123 // Pick up arguments 00124 char *filename; 00125 char *content = NULL; 00126 int ok = PyArg_ParseTuple(args, "s|s:save", &filename, &content); 00127 if (!ok) return NULL; 00128 00129 // Execute and catch exceptions 00130 Py_BEGIN_ALLOW_THREADS // Free Python interpreter for other threads 00131 try 00132 { 00133 XMLOutputFile o(filename); 00134 if (content) 00135 { 00136 if (!strcmp(content,"STANDARD")) 00137 o.setContentType(XMLOutput::STANDARD); 00138 else if (!strcmp(content,"PLAN")) 00139 o.setContentType(XMLOutput::PLAN); 00140 else if (!strcmp(content,"PLANDETAIL")) 00141 o.setContentType(XMLOutput::PLANDETAIL); 00142 else 00143 throw DataException("Invalid content type '" + string(content) + "'"); 00144 } 00145 o.writeElementWithHeader(Tags::tag_plan, &Plan::instance()); 00146 } 00147 catch (...) 00148 { 00149 Py_BLOCK_THREADS; 00150 PythonType::evalException(); 00151 return NULL; 00152 } 00153 Py_END_ALLOW_THREADS // Reclaim Python interpreter 00154 return Py_BuildValue(""); 00155 } 00156 00157 00158 // 00159 // SAVE PLAN SUMMARY TO TEXT FILE 00160 // 00161 00162 00163 DECLARE_EXPORT PyObject* savePlan(PyObject* self, PyObject* args) 00164 { 00165 // Pick up arguments 00166 const char *filename = "plan.out"; 00167 int ok = PyArg_ParseTuple(args, "s:saveplan", &filename); 00168 if (!ok) return NULL; 00169 00170 // Free Python interpreter for other threads 00171 Py_BEGIN_ALLOW_THREADS 00172 00173 // Execute and catch exceptions 00174 ofstream textoutput; 00175 try 00176 { 00177 // Open the output file 00178 textoutput.open(filename, ios::out); 00179 00180 // Write the buffer summary 00181 for (Buffer::iterator gbuf = Buffer::begin(); 00182 gbuf != Buffer::end(); ++gbuf) 00183 { 00184 if (!gbuf->getHidden()) 00185 for (Buffer::flowplanlist::const_iterator 00186 oo=gbuf->getFlowPlans().begin(); 00187 oo!=gbuf->getFlowPlans().end(); 00188 ++oo) 00189 if (oo->getType() == 1 && oo->getQuantity() != 0.0) 00190 { 00191 textoutput << "BUFFER\t" << *gbuf << '\t' 00192 << oo->getDate() << '\t' 00193 << oo->getQuantity() << '\t' 00194 << oo->getOnhand() << endl; 00195 } 00196 } 00197 00198 // Write the demand summary 00199 for (Demand::iterator gdem = Demand::begin(); 00200 gdem != Demand::end(); ++gdem) 00201 { 00202 if (!gdem->getHidden()) 00203 { 00204 for (Demand::OperationPlan_list::const_iterator 00205 pp = gdem->getDelivery().begin(); 00206 pp != gdem->getDelivery().end(); 00207 ++pp) 00208 textoutput << "DEMAND\t" << (*gdem) << '\t' 00209 << (*pp)->getDates().getEnd() << '\t' 00210 << (*pp)->getQuantity() << endl; 00211 } 00212 } 00213 00214 // Write the resource summary 00215 for (Resource::iterator gres = Resource::begin(); 00216 gres != Resource::end(); ++gres) 00217 { 00218 if (!gres->getHidden()) 00219 for (Resource::loadplanlist::const_iterator 00220 qq=gres->getLoadPlans().begin(); 00221 qq!=gres->getLoadPlans().end(); 00222 ++qq) 00223 if (qq->getType() == 1 && qq->getQuantity() != 0.0) 00224 { 00225 textoutput << "RESOURCE\t" << *gres << '\t' 00226 << qq->getDate() << '\t' 00227 << qq->getQuantity() << '\t' 00228 << qq->getOnhand() << endl; 00229 } 00230 } 00231 00232 // Write the operationplan summary. 00233 for (OperationPlan::iterator rr = OperationPlan::begin(); 00234 rr != OperationPlan::end(); ++rr) 00235 { 00236 if (rr->getOperation()->getHidden()) continue; 00237 textoutput << "OPERATION\t" << rr->getOperation() << '\t' 00238 << rr->getDates().getStart() << '\t' 00239 << rr->getDates().getEnd() << '\t' 00240 << rr->getQuantity() << endl; 00241 } 00242 00243 // Write the problem summary. 00244 for (Problem::const_iterator gprob = Problem::begin(); 00245 gprob != Problem::end(); ++gprob) 00246 { 00247 textoutput << "PROBLEM\t" << gprob->getType().type << '\t' 00248 << gprob->getDescription() << '\t' 00249 << gprob->getDates() << endl; 00250 } 00251 00252 // Write the constraint summary 00253 for (Demand::iterator gdem = Demand::begin(); 00254 gdem != Demand::end(); ++gdem) 00255 { 00256 if (!gdem->getHidden()) 00257 { 00258 for (Problem::const_iterator i = gdem->getConstraints().begin(); 00259 i != gdem->getConstraints().end(); 00260 ++i) 00261 textoutput << "DEMAND CONSTRAINT\t" << (*gdem) << '\t' 00262 << i->getDescription() << '\t' 00263 << i->getDates() << '\t' << endl; 00264 } 00265 } 00266 00267 // Close the output file 00268 textoutput.close(); 00269 } 00270 catch (...) 00271 { 00272 if (textoutput.is_open()) 00273 textoutput.close(); 00274 Py_BLOCK_THREADS; 00275 PythonType::evalException(); 00276 return NULL; 00277 } 00278 Py_END_ALLOW_THREADS // Reclaim Python interpreter 00279 return Py_BuildValue(""); 00280 } 00281 00282 00283 // 00284 // MOVE OPERATIONPLAN 00285 // 00286 00287 DECLARE_EXPORT CommandMoveOperationPlan::CommandMoveOperationPlan 00288 (OperationPlan* o) : opplan(o), firstCommand(NULL) 00289 { 00290 if (!o) 00291 { 00292 originalqty = 0; 00293 return; 00294 } 00295 originalqty = opplan->getQuantity(); 00296 originaldates = opplan->getDates(); 00297 00298 // Construct a subcommand for all suboperationplans 00299 for (OperationPlan::iterator x(o); x != o->end(); ++x) 00300 if (x->getOperation() != OperationSetup::setupoperation) 00301 { 00302 CommandMoveOperationPlan *n = new CommandMoveOperationPlan(o); 00303 n->owner = this; 00304 if (firstCommand) 00305 { 00306 n->next = firstCommand; 00307 firstCommand->prev = n; 00308 } 00309 firstCommand = n; 00310 } 00311 } 00312 00313 00314 DECLARE_EXPORT CommandMoveOperationPlan::CommandMoveOperationPlan 00315 (OperationPlan* o, Date newstart, Date newend, double newQty) 00316 : opplan(o), firstCommand(NULL) 00317 { 00318 if (!opplan) return; 00319 00320 // Store current settings 00321 originalqty = opplan->getQuantity(); 00322 if (newQty == -1.0) newQty = originalqty; 00323 originaldates = opplan->getDates(); 00324 00325 // Update the settings 00326 assert(opplan->getOperation()); 00327 opplan->getOperation()->setOperationPlanParameters( 00328 opplan, newQty, newstart, newend 00329 ); 00330 00331 // Construct a subcommand for all suboperationplans 00332 for (OperationPlan::iterator x(o); x != o->end(); ++x) 00333 if (x->getOperation() != OperationSetup::setupoperation) 00334 { 00335 CommandMoveOperationPlan *n = new CommandMoveOperationPlan(o); 00336 n->owner = this; 00337 if (firstCommand) 00338 { 00339 n->next = firstCommand; 00340 firstCommand->prev = n; 00341 } 00342 firstCommand = n; 00343 } 00344 } 00345 00346 00347 DECLARE_EXPORT void CommandMoveOperationPlan::redo() // @todo not implemented 00348 { 00349 } 00350 00351 00352 DECLARE_EXPORT void CommandMoveOperationPlan::restore(bool del) 00353 { 00354 // Restore all suboperationplans and (optionally) delete the subcommands 00355 for (Command *c = firstCommand; c; ) 00356 { 00357 CommandMoveOperationPlan *tmp = static_cast<CommandMoveOperationPlan*>(c); 00358 tmp->restore(del); 00359 c = c->next; 00360 if (del) delete tmp; 00361 } 00362 00363 // Restore the original dates 00364 if (!opplan) return; 00365 opplan->getOperation()->setOperationPlanParameters( 00366 opplan, originalqty, originaldates.getStart(), originaldates.getEnd() 00367 ); 00368 } 00369 00370 00371 // 00372 // DELETE OPERATIONPLAN 00373 // 00374 00375 DECLARE_EXPORT CommandDeleteOperationPlan::CommandDeleteOperationPlan 00376 (OperationPlan* o) : opplan(o) 00377 { 00378 // Validate input 00379 if (!o) return; 00380 00381 // Avoid deleting locked operationplans 00382 if (o->getLocked()) 00383 { 00384 opplan = NULL; 00385 throw DataException("Can't delete a locked operationplan"); 00386 } 00387 00388 // Delete all flowplans and loadplans, and unregister from operationplan list 00389 redo(); 00390 } 00391 00392 00393 // 00394 // DELETE MODEL 00395 // 00396 00397 00398 DECLARE_EXPORT PyObject* eraseModel(PyObject* self, PyObject* args) 00399 { 00400 // Pick up arguments 00401 PyObject *obj = NULL; 00402 int ok = PyArg_ParseTuple(args, "|O:erase", &obj); 00403 if (!ok) return NULL; 00404 00405 // Validate the argument 00406 bool deleteStaticModel = false; 00407 if (obj) deleteStaticModel = PythonObject(obj).getBool(); 00408 00409 // Execute and catch exceptions 00410 Py_BEGIN_ALLOW_THREADS // Free Python interpreter for other threads 00411 try 00412 { 00413 if (deleteStaticModel) 00414 { 00415 // Delete all entities. 00416 // The order is chosen to minimize the work of the individual destructors. 00417 // E.g. the destructor of the item class recurses over all demands and 00418 // all buffers. It is much faster if there are none already. 00419 Demand::clear(); 00420 Operation::clear(); 00421 Buffer::clear(); 00422 Resource::clear(); 00423 SetupMatrix::clear(); 00424 Location::clear(); 00425 Customer::clear(); 00426 Calendar::clear(); 00427 Solver::clear(); 00428 Item::clear(); 00429 // The setup operation is a static singleton and should always be around 00430 OperationSetup::setupoperation = Operation::add(new OperationSetup("setup operation")); 00431 } 00432 else 00433 // Delete the operationplans only 00434 for (Operation::iterator gop = Operation::begin(); 00435 gop != Operation::end(); ++gop) 00436 gop->deleteOperationPlans(); 00437 } 00438 catch (...) 00439 { 00440 Py_BLOCK_THREADS; 00441 PythonType::evalException(); 00442 return NULL; 00443 } 00444 Py_END_ALLOW_THREADS // Reclaim Python interpreter 00445 return Py_BuildValue(""); 00446 } 00447 00448 00449 // 00450 // PRINT MODEL SIZE 00451 // 00452 00453 00454 DECLARE_EXPORT PyObject* printModelSize(PyObject* self, PyObject* args) 00455 { 00456 // Free Python interpreter for other threads 00457 Py_BEGIN_ALLOW_THREADS 00458 00459 // Execute and catch exceptions 00460 size_t count, memsize; 00461 try 00462 { 00463 00464 // Intro 00465 logger << endl << "Size information of frePPLe " << PACKAGE_VERSION 00466 << " (" << __DATE__ << ")" << endl << endl; 00467 00468 // Print current locale 00469 #if defined(HAVE_SETLOCALE) || defined(_MSC_VER) 00470 logger << "Locale: " << setlocale(LC_ALL,NULL) << endl << endl; 00471 #else 00472 logger << endl; 00473 #endif 00474 00475 // Print loaded modules 00476 Environment::printModules(); 00477 00478 // Print the number of clusters 00479 logger << "Clusters: " << HasLevel::getNumberOfClusters() 00480 << " (hanging: " << HasLevel::getNumberOfHangingClusters() << ")" 00481 << endl << endl; 00482 00483 // Header for memory size 00484 logger << "Memory usage:" << endl; 00485 logger << "Model \tNumber\tMemory" << endl; 00486 logger << "----- \t------\t------" << endl; 00487 00488 // Plan 00489 size_t total = Plan::instance().getSize(); 00490 logger << "Plan \t1\t"<< Plan::instance().getSize() << endl; 00491 00492 // Locations 00493 memsize = 0; 00494 for (Location::iterator l = Location::begin(); l != Location::end(); ++l) 00495 memsize += l->getSize(); 00496 logger << "Location \t" << Location::size() << "\t" << memsize << endl; 00497 total += memsize; 00498 00499 // Customers 00500 memsize = 0; 00501 for (Customer::iterator c = Customer::begin(); c != Customer::end(); ++c) 00502 memsize += c->getSize(); 00503 logger << "Customer \t" << Customer::size() << "\t" << memsize << endl; 00504 total += memsize; 00505 00506 // Buffers 00507 memsize = 0; 00508 for (Buffer::iterator b = Buffer::begin(); b != Buffer::end(); ++b) 00509 memsize += b->getSize(); 00510 logger << "Buffer \t" << Buffer::size() << "\t" << memsize << endl; 00511 total += memsize; 00512 00513 // Setup matrices 00514 memsize = 0; 00515 for (SetupMatrix::iterator s = SetupMatrix::begin(); s != SetupMatrix::end(); ++s) 00516 memsize += s->getSize(); 00517 logger << "Setup matrix \t" << SetupMatrix::size() << "\t" << memsize << endl; 00518 total += memsize; 00519 00520 // Resources 00521 memsize = 0; 00522 for (Resource::iterator r = Resource::begin(); r != Resource::end(); ++r) 00523 memsize += r->getSize(); 00524 logger << "Resource \t" << Resource::size() << "\t" << memsize << endl; 00525 total += memsize; 00526 00527 // Operations, flows and loads 00528 size_t countFlows(0), memFlows(0), countLoads(0), memLoads(0); 00529 memsize = 0; 00530 for (Operation::iterator o = Operation::begin(); o != Operation::end(); ++o) 00531 { 00532 memsize += o->getSize(); 00533 for (Operation::flowlist::const_iterator fl = o->getFlows().begin(); 00534 fl != o->getFlows().end(); ++ fl) 00535 { 00536 ++countFlows; 00537 memFlows += fl->getSize(); 00538 } 00539 for (Operation::loadlist::const_iterator ld = o->getLoads().begin(); 00540 ld != o->getLoads().end(); ++ ld) 00541 { 00542 ++countLoads; 00543 memLoads += ld->getSize(); 00544 } 00545 } 00546 logger << "Operation \t" << Operation::size() << "\t" << memsize << endl; 00547 logger << "Flow \t" << countFlows << "\t" << memFlows << endl; 00548 logger << "Load \t" << countLoads << "\t" << memLoads << endl; 00549 total += memsize + memFlows + memLoads; 00550 00551 // Calendars (which includes the buckets) 00552 memsize = 0; 00553 for (Calendar::iterator cl = Calendar::begin(); cl != Calendar::end(); ++cl) 00554 memsize += cl->getSize(); 00555 logger << "Calendar \t" << Calendar::size() << "\t" << memsize << endl; 00556 total += memsize; 00557 00558 // Items 00559 memsize = 0; 00560 for (Item::iterator i = Item::begin(); i != Item::end(); ++i) 00561 memsize += i->getSize(); 00562 logger << "Item \t" << Item::size() << "\t" << memsize << endl; 00563 total += memsize; 00564 00565 // Demands 00566 memsize = 0; 00567 for (Demand::iterator dm = Demand::begin(); dm != Demand::end(); ++dm) 00568 memsize += dm->getSize(); 00569 logger << "Demand \t" << Demand::size() << "\t" << memsize << endl; 00570 total += memsize; 00571 00572 // Operationplans 00573 size_t countloadplans(0), countflowplans(0); 00574 memsize = count = 0; 00575 for (OperationPlan::iterator j = OperationPlan::begin(); 00576 j!=OperationPlan::end(); ++j) 00577 { 00578 ++count; 00579 memsize += sizeof(*j); 00580 countloadplans += j->sizeLoadPlans(); 00581 countflowplans += j->sizeFlowPlans(); 00582 } 00583 total += memsize; 00584 logger << "OperationPlan\t" << count << "\t" << memsize << endl; 00585 00586 // Flowplans 00587 memsize = countflowplans * sizeof(FlowPlan); 00588 total += memsize; 00589 logger << "FlowPlan \t" << countflowplans << "\t" << memsize << endl; 00590 00591 // Loadplans 00592 memsize = countloadplans * sizeof(LoadPlan); 00593 total += memsize; 00594 logger << "LoadPlan \t" << countloadplans << "\t" << memsize << endl; 00595 00596 // Problems 00597 memsize = count = 0; 00598 for (Problem::const_iterator pr = Problem::begin(); pr!=Problem::end(); ++pr) 00599 { 00600 ++count; 00601 memsize += pr->getSize(); 00602 } 00603 total += memsize; 00604 logger << "Problem \t" << count << "\t" << memsize << endl; 00605 00606 // TOTAL 00607 logger << "Total \t\t" << total << endl << endl; 00608 } 00609 catch (...) 00610 { 00611 Py_BLOCK_THREADS; 00612 PythonType::evalException(); 00613 return NULL; 00614 } 00615 Py_END_ALLOW_THREADS // Reclaim Python interpreter 00616 return Py_BuildValue(""); 00617 } 00618 00619 } // end namespace