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