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