model/actions.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * *
3  * Copyright (C) 2007-2013 by Johan De Taeye, frePPLe bvba *
4  * *
5  * This library is free software; you can redistribute it and/or modify it *
6  * under the terms of the GNU Affero General Public License as published *
7  * by the Free Software Foundation; either version 3 of the License, or *
8  * (at your option) any later version. *
9  * *
10  * This library is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU Affero General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU Affero General Public *
16  * License along with this program. *
17  * If not, see <http://www.gnu.org/licenses/>. *
18  * *
19  ***************************************************************************/
20 
21 #define FREPPLE_CORE
22 #include "frepple/model.h"
23 
24 namespace frepple
25 {
26 
27 //
28 // READ XML INPUT FILE
29 //
30 
31 
32 DECLARE_EXPORT PyObject* readXMLfile(PyObject* self, PyObject* args)
33 {
34  // Pick up arguments
35  char *filename = NULL;
36  int validate(1), validate_only(0);
37  PyObject *userexit = NULL;
38  int ok = PyArg_ParseTuple(args, "|siiO:readXMLfile",
39  &filename, &validate, &validate_only, &userexit);
40  if (!ok) return NULL;
41 
42  // Execute and catch exceptions
43  Py_BEGIN_ALLOW_THREADS // Free Python interpreter for other threads
44  try
45  {
46  if (!filename)
47  {
48  // Read from standard input
49  xercesc::StdInInputSource in;
50  XMLInput p;
51  if (userexit) p.setUserExit(userexit);
52  if (validate_only!=0)
53  // When no root object is passed, only the input validation happens
54  p.parse(in, NULL, true);
55  else
56  p.parse(in, &Plan::instance(), validate!=0);
57  }
58  else
59  {
60  XMLInputFile p(filename);
61  if (userexit) p.setUserExit(userexit);
62  if (validate_only!=0)
63  // Read and validate a file
64  p.parse(NULL, true);
65  else
66  // Read, execute and optionally validate a file
67  p.parse(&Plan::instance(),validate!=0);
68  }
69  }
70  catch (...)
71  {
72  Py_BLOCK_THREADS;
73  PythonType::evalException();
74  return NULL;
75  }
76  Py_END_ALLOW_THREADS // Reclaim Python interpreter
77  return Py_BuildValue("");
78 }
79 
80 
81 //
82 // READ XML INPUT STRING
83 //
84 
85 
86 DECLARE_EXPORT PyObject* readXMLdata(PyObject *self, PyObject *args)
87 {
88  // Pick up arguments
89  char *data;
90  int validate(1), validate_only(0);
91  PyObject *userexit = NULL;
92  int ok = PyArg_ParseTuple(args, "s|iiO:readXMLdata",
93  &data, &validate, &validate_only, &userexit);
94  if (!ok) return NULL;
95 
96  // Free Python interpreter for other threads
97  Py_BEGIN_ALLOW_THREADS
98 
99  // Execute and catch exceptions
100  try
101  {
102  if (!data) throw DataException("No input data");
103  XMLInputString p(data);
104  if (userexit) p.setUserExit(userexit);
105  if (validate_only!=0)
106  p.parse(NULL, true);
107  else
108  p.parse(&Plan::instance(), validate!=0);
109  }
110  catch (...)
111  {
112  Py_BLOCK_THREADS;
113  PythonType::evalException();
114  return NULL;
115  }
116  Py_END_ALLOW_THREADS // Reclaim Python interpreter
117  return Py_BuildValue(""); // Safer than using Py_None, which is not portable across compilers
118 }
119 
120 
121 //
122 // SAVE MODEL TO XML
123 //
124 
125 
126 PyObject* saveXMLfile(PyObject* self, PyObject* args)
127 {
128  // Pick up arguments
129  char *filename;
130  char *content = NULL;
131  int ok = PyArg_ParseTuple(args, "s|s:save", &filename, &content);
132  if (!ok) return NULL;
133 
134  // Execute and catch exceptions
135  Py_BEGIN_ALLOW_THREADS // Free Python interpreter for other threads
136  try
137  {
138  XMLOutputFile o(filename);
139  if (content)
140  {
141  if (!strcmp(content,"STANDARD"))
142  o.setContentType(XMLOutput::STANDARD);
143  else if (!strcmp(content,"PLAN"))
144  o.setContentType(XMLOutput::PLAN);
145  else if (!strcmp(content,"PLANDETAIL"))
146  o.setContentType(XMLOutput::PLANDETAIL);
147  else
148  throw DataException("Invalid content type '" + string(content) + "'");
149  }
151  }
152  catch (...)
153  {
154  Py_BLOCK_THREADS;
155  PythonType::evalException();
156  return NULL;
157  }
158  Py_END_ALLOW_THREADS // Reclaim Python interpreter
159  return Py_BuildValue("");
160 }
161 
162 
163 //
164 // SAVE PLAN SUMMARY TO TEXT FILE
165 //
166 
167 
168 DECLARE_EXPORT PyObject* savePlan(PyObject* self, PyObject* args)
169 {
170  // Pick up arguments
171  const char *filename = "plan.out";
172  int ok = PyArg_ParseTuple(args, "s:saveplan", &filename);
173  if (!ok) return NULL;
174 
175  // Free Python interpreter for other threads
176  Py_BEGIN_ALLOW_THREADS
177 
178  // Execute and catch exceptions
179  ofstream textoutput;
180  try
181  {
182  // Open the output file
183  textoutput.open(filename, ios::out);
184 
185  // Write the buffer summary
186  for (Buffer::iterator gbuf = Buffer::begin();
187  gbuf != Buffer::end(); ++gbuf)
188  {
189  if (!gbuf->getHidden())
190  for (Buffer::flowplanlist::const_iterator
191  oo=gbuf->getFlowPlans().begin();
192  oo!=gbuf->getFlowPlans().end();
193  ++oo)
194  if (oo->getType() == 1 && oo->getQuantity() != 0.0)
195  {
196  textoutput << "BUFFER\t" << *gbuf << '\t'
197  << oo->getDate() << '\t'
198  << oo->getQuantity() << '\t'
199  << oo->getOnhand() << endl;
200  }
201  }
202 
203  // Write the demand summary
204  for (Demand::iterator gdem = Demand::begin();
205  gdem != Demand::end(); ++gdem)
206  {
207  if (!gdem->getHidden())
208  {
209  for (Demand::OperationPlan_list::const_iterator
210  pp = gdem->getDelivery().begin();
211  pp != gdem->getDelivery().end();
212  ++pp)
213  textoutput << "DEMAND\t" << (*gdem) << '\t'
214  << (*pp)->getDates().getEnd() << '\t'
215  << (*pp)->getQuantity() << endl;
216  }
217  }
218 
219  // Write the resource summary
220  for (Resource::iterator gres = Resource::begin();
221  gres != Resource::end(); ++gres)
222  {
223  if (!gres->getHidden())
224  for (Resource::loadplanlist::const_iterator
225  qq=gres->getLoadPlans().begin();
226  qq!=gres->getLoadPlans().end();
227  ++qq)
228  if (qq->getType() == 1 && qq->getQuantity() != 0.0)
229  {
230  textoutput << "RESOURCE\t" << *gres << '\t'
231  << qq->getDate() << '\t'
232  << qq->getQuantity() << '\t'
233  << qq->getOnhand() << endl;
234  }
235  }
236 
237  // Write the operationplan summary.
239  rr != OperationPlan::end(); ++rr)
240  {
241  if (rr->getOperation()->getHidden()) continue;
242  textoutput << "OPERATION\t" << rr->getOperation() << '\t'
243  << rr->getDates().getStart() << '\t'
244  << rr->getDates().getEnd() << '\t'
245  << rr->getQuantity() << endl;
246  }
247 
248  // Write the problem summary.
250  gprob != Problem::end(); ++gprob)
251  {
252  textoutput << "PROBLEM\t" << gprob->getType().type << '\t'
253  << gprob->getDescription() << '\t'
254  << gprob->getDates() << endl;
255  }
256 
257  // Write the constraint summary
258  for (Demand::iterator gdem = Demand::begin();
259  gdem != Demand::end(); ++gdem)
260  {
261  if (!gdem->getHidden())
262  {
263  for (Problem::const_iterator i = gdem->getConstraints().begin();
264  i != gdem->getConstraints().end();
265  ++i)
266  textoutput << "DEMAND CONSTRAINT\t" << (*gdem) << '\t'
267  << i->getDescription() << '\t'
268  << i->getDates() << '\t' << endl;
269  }
270  }
271 
272  // Close the output file
273  textoutput.close();
274  }
275  catch (...)
276  {
277  if (textoutput.is_open())
278  textoutput.close();
279  Py_BLOCK_THREADS;
280  PythonType::evalException();
281  return NULL;
282  }
283  Py_END_ALLOW_THREADS // Reclaim Python interpreter
284  return Py_BuildValue("");
285 }
286 
287 
288 //
289 // MOVE OPERATIONPLAN
290 //
291 
293 (OperationPlan* o) : opplan(o), firstCommand(NULL)
294 {
295  if (!o)
296  {
297  originalqty = 0;
298  return;
299  }
300  originalqty = opplan->getQuantity();
301  originaldates = opplan->getDates();
302 
303  // Construct a subcommand for all suboperationplans
304  for (OperationPlan::iterator x(o); x != o->end(); ++x)
305  if (x->getOperation() != OperationSetup::setupoperation)
306  {
308  n->owner = this;
309  if (firstCommand)
310  {
311  n->next = firstCommand;
312  firstCommand->prev = n;
313  }
314  firstCommand = n;
315  }
316 }
317 
318 
320 (OperationPlan* o, Date newstart, Date newend, double newQty)
321  : opplan(o), firstCommand(NULL)
322 {
323  if (!opplan) return;
324 
325  // Store current settings
326  originalqty = opplan->getQuantity();
327  if (newQty == -1.0) newQty = originalqty;
328  originaldates = opplan->getDates();
329 
330  // Update the settings
331  assert(opplan->getOperation());
332  opplan->getOperation()->setOperationPlanParameters(
333  opplan, newQty, newstart, newend
334  );
335 
336  // Construct a subcommand for all suboperationplans
337  for (OperationPlan::iterator x(o); x != o->end(); ++x)
338  if (x->getOperation() != OperationSetup::setupoperation)
339  {
341  n->owner = this;
342  if (firstCommand)
343  {
344  n->next = firstCommand;
345  firstCommand->prev = n;
346  }
347  firstCommand = n;
348  }
349 }
350 
351 
352 DECLARE_EXPORT void CommandMoveOperationPlan::redo() // @todo not implemented
353 {
354 }
355 
356 
358 {
359  // Restore all suboperationplans and (optionally) delete the subcommands
360  for (Command *c = firstCommand; c; )
361  {
362  CommandMoveOperationPlan *tmp = static_cast<CommandMoveOperationPlan*>(c);
363  tmp->restore(del);
364  c = c->next;
365  if (del) delete tmp;
366  }
367 
368  // Restore the original dates
369  if (!opplan) return;
371  opplan, originalqty, originaldates.getStart(), originaldates.getEnd()
372  );
373 }
374 
375 
376 //
377 // DELETE OPERATIONPLAN
378 //
379 
381 (OperationPlan* o) : opplan(o)
382 {
383  // Validate input
384  if (!o) return;
385 
386  // Avoid deleting locked operationplans
387  if (o->getLocked())
388  {
389  opplan = NULL;
390  throw DataException("Can't delete a locked operationplan");
391  }
392 
393  // Delete all flowplans and loadplans, and unregister from operationplan list
394  redo();
395 }
396 
397 
398 //
399 // DELETE MODEL
400 //
401 
402 
403 DECLARE_EXPORT PyObject* eraseModel(PyObject* self, PyObject* args)
404 {
405  // Pick up arguments
406  PyObject *obj = NULL;
407  int ok = PyArg_ParseTuple(args, "|O:erase", &obj);
408  if (!ok) return NULL;
409 
410  // Validate the argument
411  bool deleteStaticModel = false;
412  if (obj) deleteStaticModel = PythonObject(obj).getBool();
413 
414  // Execute and catch exceptions
415  Py_BEGIN_ALLOW_THREADS // Free Python interpreter for other threads
416  try
417  {
418  if (deleteStaticModel)
419  {
420  // Delete all entities.
421  // The order is chosen to minimize the work of the individual destructors.
422  // E.g. the destructor of the item class recurses over all demands and
423  // all buffers. It is much faster if there are none already.
425  Demand::clear();
426  Buffer::clear();
427  Resource::clear();
429  Location::clear();
430  Customer::clear();
431  Calendar::clear();
432  Solver::clear();
433  Item::clear();
434  // The setup operation is a static singleton and should always be around
436  }
437  else
438  // Delete the operationplans only
439  for (Operation::iterator gop = Operation::begin();
440  gop != Operation::end(); ++gop)
441  gop->deleteOperationPlans();
442  }
443  catch (...)
444  {
445  Py_BLOCK_THREADS;
446  PythonType::evalException();
447  return NULL;
448  }
449  Py_END_ALLOW_THREADS // Reclaim Python interpreter
450  return Py_BuildValue("");
451 }
452 
453 
454 //
455 // PRINT MODEL SIZE
456 //
457 
458 
459 DECLARE_EXPORT PyObject* printModelSize(PyObject* self, PyObject* args)
460 {
461  // Free Python interpreter for other threads
462  Py_BEGIN_ALLOW_THREADS
463 
464  // Execute and catch exceptions
465  size_t count, memsize;
466  try
467  {
468 
469  // Intro
470  logger << endl << "Size information of frePPLe " << PACKAGE_VERSION
471  << " (" << __DATE__ << ")" << endl << endl;
472 
473  // Print current locale
474 #if defined(HAVE_SETLOCALE) || defined(_MSC_VER)
475  logger << "Locale: " << setlocale(LC_ALL,NULL) << endl << endl;
476 #else
477  logger << endl;
478 #endif
479 
480  // Print loaded modules
481  Environment::printModules();
482 
483  // Print the number of clusters
484  logger << "Clusters: " << HasLevel::getNumberOfClusters()
485  << " (hanging: " << HasLevel::getNumberOfHangingClusters() << ")"
486  << endl << endl;
487 
488  // Header for memory size
489  logger << "Memory usage:" << endl;
490  logger << "Model \tNumber\tMemory" << endl;
491  logger << "----- \t------\t------" << endl;
492 
493  // Plan
494  size_t total = Plan::instance().getSize();
495  logger << "Plan \t1\t"<< Plan::instance().getSize() << endl;
496 
497  // Locations
498  memsize = 0;
499  for (Location::iterator l = Location::begin(); l != Location::end(); ++l)
500  memsize += l->getSize();
501  logger << "Location \t" << Location::size() << "\t" << memsize << endl;
502  total += memsize;
503 
504  // Customers
505  memsize = 0;
506  for (Customer::iterator c = Customer::begin(); c != Customer::end(); ++c)
507  memsize += c->getSize();
508  logger << "Customer \t" << Customer::size() << "\t" << memsize << endl;
509  total += memsize;
510 
511  // Buffers
512  memsize = 0;
513  for (Buffer::iterator b = Buffer::begin(); b != Buffer::end(); ++b)
514  memsize += b->getSize();
515  logger << "Buffer \t" << Buffer::size() << "\t" << memsize << endl;
516  total += memsize;
517 
518  // Setup matrices
519  memsize = 0;
520  for (SetupMatrix::iterator s = SetupMatrix::begin(); s != SetupMatrix::end(); ++s)
521  memsize += s->getSize();
522  logger << "Setup matrix \t" << SetupMatrix::size() << "\t" << memsize << endl;
523  total += memsize;
524 
525  // Resources
526  memsize = 0;
527  for (Resource::iterator r = Resource::begin(); r != Resource::end(); ++r)
528  memsize += r->getSize();
529  logger << "Resource \t" << Resource::size() << "\t" << memsize << endl;
530  total += memsize;
531 
532  // Skills and resourceskills
533  size_t countResourceSkills(0), memResourceSkills(0);
534  memsize = 0;
535  for (Skill::iterator sk = Skill::begin(); sk != Skill::end(); ++sk)
536  {
537  memsize += sk->getSize();
538  for (Skill::resourcelist::const_iterator rs = sk->getResources().begin();
539  rs != sk->getResources().end(); ++rs)
540  {
541  ++countResourceSkills;
542  memResourceSkills += rs->getSize();
543  }
544  }
545  logger << "Skill \t" << Skill::size() << "\t" << memsize << endl;
546  logger << "ResourceSkill \t" << countResourceSkills << "\t" << memResourceSkills << endl;
547  total += memsize;
548 
549  // Operations, flows and loads
550  size_t countFlows(0), memFlows(0), countLoads(0), memLoads(0);
551  memsize = 0;
552  for (Operation::iterator o = Operation::begin(); o != Operation::end(); ++o)
553  {
554  memsize += o->getSize();
555  for (Operation::flowlist::const_iterator fl = o->getFlows().begin();
556  fl != o->getFlows().end(); ++ fl)
557  {
558  ++countFlows;
559  memFlows += fl->getSize();
560  }
561  for (Operation::loadlist::const_iterator ld = o->getLoads().begin();
562  ld != o->getLoads().end(); ++ ld)
563  {
564  ++countLoads;
565  memLoads += ld->getSize();
566  }
567  }
568  logger << "Operation \t" << Operation::size() << "\t" << memsize << endl;
569  logger << "Flow \t" << countFlows << "\t" << memFlows << endl;
570  logger << "Load \t" << countLoads << "\t" << memLoads << endl;
571  total += memsize + memFlows + memLoads;
572 
573  // Calendars (which includes the buckets)
574  memsize = 0;
575  for (Calendar::iterator cl = Calendar::begin(); cl != Calendar::end(); ++cl)
576  memsize += cl->getSize();
577  logger << "Calendar \t" << Calendar::size() << "\t" << memsize << endl;
578  total += memsize;
579 
580  // Items
581  memsize = 0;
582  for (Item::iterator i = Item::begin(); i != Item::end(); ++i)
583  memsize += i->getSize();
584  logger << "Item \t" << Item::size() << "\t" << memsize << endl;
585  total += memsize;
586 
587  // Demands
588  memsize = 0;
589  size_t c_count = 0, c_memsize = 0;
590  for (Demand::iterator dm = Demand::begin(); dm != Demand::end(); ++dm)
591  {
592  memsize += dm->getSize();
593  for (Problem::const_iterator cstrnt(dm->getConstraints().begin());
594  cstrnt != dm->getConstraints().end(); ++cstrnt)
595  {
596  ++c_count;
597  c_memsize += cstrnt->getSize();
598  }
599  }
600  logger << "Demand \t" << Demand::size() << "\t" << memsize << endl;
601  logger << "Constraints \t" << c_count << "\t" << c_memsize << endl;
602  total += memsize + c_memsize;
603 
604  // Operationplans
605  size_t countloadplans(0), countflowplans(0);
606  memsize = count = 0;
608  j!=OperationPlan::end(); ++j)
609  {
610  ++count;
611  memsize += sizeof(*j);
612  countloadplans += j->sizeLoadPlans();
613  countflowplans += j->sizeFlowPlans();
614  }
615  total += memsize;
616  logger << "OperationPlan\t" << count << "\t" << memsize << endl;
617 
618  // Flowplans
619  memsize = countflowplans * sizeof(FlowPlan);
620  total += memsize;
621  logger << "FlowPlan \t" << countflowplans << "\t" << memsize << endl;
622 
623  // Loadplans
624  memsize = countloadplans * sizeof(LoadPlan);
625  total += memsize;
626  logger << "LoadPlan \t" << countloadplans << "\t" << memsize << endl;
627 
628  // Problems
629  memsize = count = 0;
630  for (Problem::const_iterator pr = Problem::begin(); pr!=Problem::end(); ++pr)
631  {
632  ++count;
633  memsize += pr->getSize();
634  }
635  total += memsize;
636  logger << "Problem \t" << count << "\t" << memsize << endl;
637 
638  // TOTAL
639  logger << "Total \t\t" << total << endl << endl;
640  }
641  catch (...)
642  {
643  Py_BLOCK_THREADS;
644  PythonType::evalException();
645  return NULL;
646  }
647  Py_END_ALLOW_THREADS // Reclaim Python interpreter
648  return Py_BuildValue("");
649 }
650 
651 } // end namespace