simlpy.cc

00001 // Copyright 2008 Michael Marsh, University of Maryland.
00002 //
00003 // This file is part of pydtn.
00004 //
00005 // pydtn is free software: you can redistribute it and/or modify
00006 // it under the terms of the GNU General Public License as published by
00007 // the Free Software Foundation, either version 3 of the License, or
00008 // (at your option) any later version.
00009 //
00010 // pydtn is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 // GNU General Public License for more details.
00014 //
00015 // You should have received a copy of the GNU General Public License
00016 // along with pydtn.  If not, see <http://www.gnu.org/licenses/>.
00017 //
00018 // The views and conclusions contained in the software and documentation
00019 // are those of the authors and should not be interpreted as representing
00020 // official policies, either expressed or implied, of the University
00021 // of Maryland.
00022 //
00023 // pydtn extends and embeds the Python interpreter, which is
00024 // Copyright 2001-2006 Python Software Foundation, All Rights Reserved,
00025 // and is released under the PSF License Agreement.
00026 //
00027 // RANLUX random number generation uses the Boost library,
00028 // Copyright 1994-2006 by various authors (details in individual files),
00029 // which is released under the Boost Software License, Version 1.0.
00030 
00031 // Python is *so* *important* that its header *must* be loaded *before*
00032 // *any* *other* headers.
00033 #include "interpreter_defs.h"
00034 
00035 #include "config.h"
00036 #include "simlpy.h"
00037 
00038 #include "Entity.h"
00039 #include "Registry.h"
00040 #include "Terminator.h"
00041 #include "InterpreterEvent.h"
00042 #include "InterpreterEntity.h"
00043 #include "SimulationException.h"
00044 
00045 #include "boost/random/ranlux.hpp"
00046 
00047 #include <iostream>
00048 #include <vector>
00049 #include <queue>
00050 #include <string>
00051 #include <unistd.h>
00052 #include <stdio.h>
00053 
00056 
00057 #ifndef PyMODINIT_FUNC
00060 #define PyMODINIT_FUNC extern "C" void
00061 #endif
00062 
00063 //==============================================================
00066 
00071 typedef struct
00072 {
00073       PyObject_HEAD
00074       PyObject* type; 
00075       Entity* e;      
00076       int id;         
00077 } PySimEntity;
00078 
00083 static char PySimEntity_doc[] = "Simulation entity.\n\
00084    \n\
00085    An Entity is created by calling\n\
00086    \n\
00087       sim.Entity(<type>)\n\
00088    \n\
00089    where <type> is a string corresponding to the unique identifier\n\
00090    registered for a particular subclass.\n\
00091    \n\
00092    The string representation of an Entity is an integer.  While the\n\
00093    Entity cannot be constructed using this value, it does provide a\n\
00094    usable handle within the simulator for retrieving the object\n\
00095    from within the C++ framework.\n";
00096 
00108 static PyObject*
00109 PySimEntity_id( PyObject* self )
00110 {
00111    return PyString_FromFormat("%d",((PySimEntity*)self)->id);
00112 }
00113 
00122 static void
00123 PySimEntity_dealloc( PySimEntity* self )
00124 {
00125    Py_XDECREF( self->type );
00126    // WE DO NOT DEALLOCATE THE Entity!
00127    self->ob_type->tp_free((PyObject*)self);
00128 }
00129 
00130 void
00131 clean_arglist( Entity::ArgList& alist )
00132 {
00133    Entity::ArgList::iterator itr = alist.begin();
00134    Entity::ArgList::iterator end = alist.end();
00135    for ( ; itr != end ; ++itr )
00136    {
00137       Py_DECREF(*itr);
00138    }
00139 }
00140 
00141 void
00142 build_arglist( Entity::ArgList& alist, PyObject* args )
00143 {
00144    if ( 0 == args ) return;
00145    if ( ! PyTuple_Check( args ) )
00146    {
00147       PyErr_SetString(PyExc_TypeError,"config expects a list of arguments");
00148       throw SimulationException( __FILE__ , __LINE__ );
00149    }
00150    int n = PyTuple_Size( args );
00151    for ( int i = 0 ; i < n ; ++i )
00152    {
00153       PyObject* p = PyTuple_GetItem( args, i );
00154       if ( 0 == p )
00155       {
00156          PyErr_SetString(PyExc_TypeError,"missing argument in list");
00157          throw SimulationException( __FILE__ , __LINE__ );
00158       }
00159       Py_INCREF(p);
00160       alist.push_back(p);
00161    }
00162 }
00163 
00167 static char PySimEntity_config_doc[] = "Configure the Entity.\n\
00168    \n\
00169    Calling syntax for a sim.Entity e:\n\
00170       e.config(<arg1>,<arg2>,...)\n\
00171    \n\
00172    We perform no type checking on the arguments, other than that\n\
00173    they are in fact a list.  All Python objects passed to this\n\
00174    method are converted first to Python strings and then to C++\n\
00175    strings.  The latter are passed to Entity::configure() through\n\
00176    an Entity::ArgList.\n\
00177    \n\
00178    It is up to specific subclasses to interpret the argument list.\n\
00179    Modules that implement concrete subclasses will document valid\n\
00180    calls to e.config() for those classes.\n";
00181 
00191 static PyObject*
00192 PySimEntity_config( PySimEntity* self, PyObject* args )
00193 {
00194    Entity::ArgList entityArgs;
00195    try
00196    {
00197       build_arglist( entityArgs, args );
00198       self->e->configure( entityArgs );
00199       clean_arglist(entityArgs);
00200    }
00201    catch ( ... )
00202    {
00203       PyErr_SetString(PyExc_ValueError,
00204                       "Bad argument list passed to sim.config");
00205       clean_arglist(entityArgs);
00206       return 0;
00207    }
00208    Py_INCREF(self);
00209    return (PyObject*)self;
00210 }
00211 
00215 static char PySimEntity_emit_doc[] = "Have the Entity emit an Event.\n\
00216    \n\
00217    Calling syntax for a sim.Entity e:\n\
00218       e.emit(<arg1>,<arg2>,...)\n\
00219    \n\
00220    We perform no type checking on the arguments, other than that\n\
00221    they are in fact a list.  All Python objects passed to this\n\
00222    method are converted first to Python strings and then to C++\n\
00223    strings.  The latter are passed to Entity::emit() through an\n\
00224    Entity::ArgList.\n\
00225    \n\
00226    It is up to specific subclasses to interpret the argument list.\n\
00227    Modules that implement concrete subclasses will document valid\n\
00228    calls to e.config() for those classes.\n";
00229 
00239 static PyObject*
00240 PySimEntity_emit( PySimEntity* self, PyObject* args )
00241 {
00242    Entity::ArgList entityArgs;
00243    try
00244    {
00245       build_arglist( entityArgs, args );
00246       self->e->emit( entityArgs );
00247       clean_arglist(entityArgs);
00248    }
00249    catch ( ... )
00250    {
00251       PyErr_SetString(PyExc_ValueError,
00252                       "Bad argument list passed to sim.emit");
00253       clean_arglist(entityArgs);
00254       return 0;
00255    }
00256    Py_INCREF(self);
00257    return (PyObject*)self;
00258 }
00259 
00263 static char PySimEntity_get_doc[] = "Get Entity attributes.\n\
00264    \n\
00265    Calling syntax for a sim.Entity e:\n\
00266       e.get(<arg1>,<arg2>,...)\n\
00267    \n\
00268    The arguments are generally strings, specifying Entity\n\
00269    attributes to query.  The specific Entity subclass determines\n\
00270    how to interpret the arguments, so fairly complex behavior can\n\
00271    be implemented through this mechanism.\n\
00272    \n\
00273    This method returns something appropriate for the arguments\n\
00274    provided. The type of this return value is completely\n\
00275    subclass-dependent.\n\
00276    \n\
00277    Modules that implement concrete subclasses will document\n\
00278    valid calls to e.get() for those classes.\n";
00279 
00289 static PyObject*
00290 PySimEntity_get( PySimEntity* self, PyObject* args )
00291 {
00292    Entity::ArgList entityArgs;
00293    PyObject* retval = 0;
00294    try
00295    {
00296       build_arglist( entityArgs, args );
00297       retval = self->e->get( entityArgs ); // New reference
00298       clean_arglist(entityArgs);
00299    }
00300    catch ( ... )
00301    {
00302       PyErr_SetString(PyExc_ValueError,
00303                       "Bad argument list passed to sim.config");
00304       clean_arglist(entityArgs);
00305       return 0;
00306    }
00307    return retval;
00308 }
00309 
00312 static PyMethodDef PySimEntity_methods[] =
00313 {
00314    { "config",
00315      (PyCFunction)PySimEntity_config,
00316      METH_VARARGS,
00317      PySimEntity_config_doc },
00318    { "emit",
00319      (PyCFunction)PySimEntity_emit,
00320      METH_VARARGS,
00321      PySimEntity_emit_doc },
00322    { "get",
00323      (PyCFunction)PySimEntity_get,
00324      METH_VARARGS,
00325      PySimEntity_get_doc },
00326    {0} // sentinel
00327 };
00328 
00331 static PyMemberDef PySimEntity_members[] =
00332 {
00333    {"type", T_OBJECT_EX, offsetof(PySimEntity, type), 0, "Entity type"},
00334    {0} // sentinel
00335 };
00336 
00339 static PyTypeObject sim_PySimEntity = {
00340    PyObject_HEAD_INIT(0)
00341    0,                         // ob_size
00342    "sim.Entity",              // tp_name
00343    sizeof(PySimEntity),       // tp_basicsize
00344    0,                         // tp_itemsize
00345    (destructor)PySimEntity_dealloc, // tp_dealloc
00346    0,                         // tp_print
00347    0,                         // tp_getattr
00348    0,                         // tp_setattr
00349    0,                         // tp_compare
00350    PySimEntity_id,            // tp_repr
00351    0,                         // tp_as_number
00352    0,                         // tp_as_sequence
00353    0,                         // tp_as_mapping
00354    0,                         // tp_hash
00355    0,                         // tp_call
00356    0,                         // tp_str
00357    0,                         // tp_getattro
00358    0,                         // tp_setattro
00359    0,                         // tp_as_buffer
00360    Py_TPFLAGS_DEFAULT,        // tp_flags
00361    PySimEntity_doc,           // tp_doc
00362    0,                         // tp_traverse
00363    0,                         // tp_clear
00364    0,                         // tp_richcompare
00365    0,                         // tp_weaklistoffset
00366    0,                         // tp_iter
00367    0,                         // tp_iternext
00368    PySimEntity_methods,       // tp_methods
00369    PySimEntity_members,       // tp_members
00370    0,                         // tp_getset
00371    0,                         // tp_base
00372    0,                         // tp_dict
00373    0,                         // tp_descr_get
00374    0,                         // tp_descr_set
00375    0,                         // tp_dictoffset
00376    0,                         // tp_init
00377    0,                         // tp_alloc
00378    0,                         // tp_new
00379 };
00380 
00382 
00383 //==============================================================
00386 
00388 typedef std::vector< PySimEntity* > SimEntityVec;
00389 
00395 static SimEntityVec entityList;
00396 
00399 static Terminator kronos;
00400 
00403 static InterpreterEntity iEntity;
00404 
00405 static PyObject* sim_args;
00406 
00408 
00409 
00426 static PyObject*
00427 sim_entity( PyTypeObject* type, PyObject* args, PyObject* kwds )
00428 {
00429    const char* eType;
00430    if ( ! PyArg_ParseTuple(args, "s", &eType) )
00431    {
00432       std::cerr << "Incorrect argument list" << std::endl;
00433       return 0;
00434    }
00435    const Entity* e = 0;
00436    try
00437    {
00438       e = Registry::lookup( eType );
00439    }
00440    catch ( ... )
00441    {
00442       std::string errString = "No entity type \"";
00443       errString += eType;
00444       errString += "\" registered";
00445       PyErr_SetString(PyExc_KeyError,errString.c_str());
00446       return 0;
00447    }
00448    Entity* newE = e->create();
00449    if ( 0 == newE )
00450    {
00451       std::cerr << "Could not allocate a new " << eType << std::endl;
00452       return PyErr_NoMemory();
00453    }
00454 
00455    return export_entity(newE);
00456 }
00457 
00458 //==============================================================
00461 
00465 static char sim_time_doc[] = "Get the current simulator time.\n\
00466    \n\
00467    This function, which takes no arguments, returns a list of\n\
00468    the form:\n\
00469    \n\
00470       [ seconds , microseconds ]\n\
00471    \n\
00472    corresponding to the current time in the simulator.\n\
00473    \n\
00474    If no events have been processed since starting simlpy, this\n\
00475    will have the value\n\
00476    \n\
00477       [0L, 0L]\n\
00478    \n\
00479    Otherwise, it will typically return the time of the last-\n\
00480    scheduled sim.stopAt() call.\n";
00481 
00492 static PyObject*
00493 sim_time( PyObject* self, PyObject* args )
00494 {
00495    return construct_time( Clock::time() );
00496 }
00497 
00501 static char sim_timeAsDouble_doc[] = "Get the current simulator time.\n\
00502    \n\
00503    This function, which takes no arguments, returns the current\n\
00504    time in seconds.\n\
00505    \n\
00506    If no events have been processed since starting simlpy, this\n\
00507    will have the value\n\
00508    \n\
00509       [0L, 0L]\n\
00510    \n\
00511    Otherwise, it will typically return the time of the last-\n\
00512    scheduled sim.stopAt() call.\n";
00513 
00524 static PyObject*
00525 sim_timeAsDouble( PyObject* self, PyObject* args )
00526 {
00527    const Time& t = Clock::time();
00528    double dt = t.tv.tv_sec + 1.0*t.tv.tv_usec/Time::MILLION;
00529    return construct_double(dt);
00530 }
00531 
00535 static char sim_run_doc[] = "Start a simulation run.\n\
00536    \n\
00537    This is, in effect, the main loop of the simulator.  As long\n\
00538    as the simulator is still in its running state, this routine\n\
00539    will continue to consume simulation events.  The loop will exit\n\
00540    when there are no more events to process or when it processes\n\
00541    an event scheduled by calling sim.stopAt().\n";
00542 
00551 static PyObject*
00552 sim_run( PyObject* self, PyObject* args )
00553 {
00554    Clock::setRunning( true );
00555    while ( Clock::running() )
00556    {
00557       Clock::tick();
00558    }
00559    Py_INCREF(Py_None);
00560    return Py_None;
00561 }
00562 
00566 static char sim_stopAt_doc[] = "Set an end time for the simulation.\n\
00567    \n\
00568    Calling syntax:\n\
00569       sim.stopAt( [<s>,<us>] )\n\
00570    \n\
00571    The list provided is a simulation time expressed in\n\
00572    seconds and microseconds.  Calling this function schedules\n\
00573    an event for this time that will cause the simulation to stop.\n\
00574    \n\
00575    Stopping the simulation does not cause simlpy to exit.  Rather,\n\
00576    it continues processing input.  A subsequent call to sim.run()\n\
00577    will resume the simulation from the point at which it stopped.\n\
00578    If there is no more input, and a sys.exit() or similar has not\n\
00579    been processed, control is returned to the user at an interactive\n\
00580    prompt.\n";
00581 
00591 static PyObject*
00592 sim_stopAt( PyObject* self, PyObject* args )
00593 {
00594    try
00595    {
00596       kronos.stopAt(parse_time(PyTuple_GetItem(args,0)));
00597    }
00598    catch( ... )
00599    {
00600       return 0;
00601    }
00602    Py_INCREF(Py_None);
00603    return Py_None;
00604 }
00605 
00609 static char sim_collect_doc[] = "Collect information from Entitys.\n\
00610    \n\
00611    When called, this will loop over all Entitys created by the\n\
00612    sim module, and will run that object's collect() method\n\
00613    in C++.  The default behavior for an Entity is to do nothing.\n\
00614    \n\
00615    See the documentation for a specific module for the behavior\n\
00616    of its Entity subclasses when this is called.\n";
00617 
00628 static PyObject*
00629 sim_collect( PyObject* self, PyObject* args )
00630 {
00631    SimEntityVec::iterator itr = entityList.begin();
00632    SimEntityVec::iterator end = entityList.end();
00633    for ( ; itr != end ; ++itr )
00634    {
00635       if ( 0 != (*itr)->e )
00636       {
00637          (*itr)->e->collect();
00638       }
00639    }
00640    Py_INCREF(Py_None);
00641    return Py_None;
00642 }
00643 
00644 static boost::ranlux3_01 sim_prng;
00645 
00646 void
00647 seed_prng( unsigned long int s )
00648 {
00649    sim_prng.seed(s);
00650 }
00651 
00652 float
00653 prng()
00654 {
00655    return sim_prng();
00656 }
00657 
00661 static char sim_seed_doc[] = "Seed the random number generator.\n\
00662    \n\
00663    Calling syntax:\n\
00664       sim.seed( i )\n\
00665       sim.seed()\n\
00666    \n\
00667    When called, this will seed the random number generator\n\
00668    with the supplied integer i.  If no seed value is given,\n\
00669    one will be selected and reported on the console.\n";
00670 
00680 static PyObject*
00681 sim_seed( PyObject* self, PyObject* args )
00682 {
00683    unsigned int s;
00684    Entity::ArgList alist;
00685    build_arglist( alist, args );
00686    if ( alist.empty() )
00687    {
00688       // not a perfect match, but if we have higher-order bytes, we
00689       // really don't care
00690       union time_int
00691       {
00692             int i;
00693             char c[4];
00694       };
00695       struct timeval tv;
00696       gettimeofday(&tv,0);
00697       time_int seconds;
00698       seconds.i = tv.tv_sec;
00699       time_int useconds;
00700       useconds.i = tv.tv_usec;
00701       time_int seed;
00702       seed.i = 0;
00703       // All bytes will change fairly quickly, though some of the bits
00704       // will be a little slower.  This really isn't a problem,
00705       // though.
00706       seed.c[0] = seconds.c[0] ^ seconds.c[3] ^ useconds.c[0];
00707       seed.c[1] = seconds.c[1] ^ seconds.c[2] ^ useconds.c[1];
00708       seed.c[2] = seconds.c[2] ^ seconds.c[1] ^ useconds.c[2];
00709       seed.c[3] = seconds.c[3] ^ seconds.c[0] ^ useconds.c[3];
00710       s = seed.i;
00711       seed_prng( s );
00712       std::cout << "Random number seed = " << s
00713                 << " (" << std::hex << s << ")" << std::dec
00714                 << std::endl;
00715    }
00716    else
00717    {
00718       try
00719       {
00720          s = parse_long(alist[0]);
00721          seed_prng( s );
00722       }
00723       catch ( ... )
00724       {
00725          std::cerr << "usage: sim.seed( i )" << std::endl;
00726          return 0;
00727       }
00728    }
00729    return construct_ulong(s);
00730 }
00731 
00735 static char sim_random_doc[] = "Call the random number generator.\n\
00736    \n\
00737    Calling syntax:\n\
00738       r = sim.random()\n\
00739    \n\
00740    When called, this will return the next pseudo-random number\n\
00741    in the sequence, as a float in the range [0,1).\n";
00742 
00753 static PyObject*
00754 sim_random( PyObject* self, PyObject* args )
00755 {
00756    return construct_double( prng() );
00757 }
00758 
00762 static char sim_get_args_doc[] = "Get command-line arguments.\n\
00763    \n\
00764    Calling syntax:\n\
00765       l = sim.args()\n\
00766    \n\
00767    When called, this returns a list of the arguments supplied to\n\
00768    simlpy on the command line with the \"-a\" flag.  All arguments\n\
00769    are returned as strings, and must be converted to other types\n\
00770    as appropriate.\n";
00771 
00781 static PyObject*
00782 sim_get_args( PyObject* self, PyObject* args )
00783 {
00784    Py_INCREF(sim_args);
00785    return sim_args;
00786 }
00787 
00791 static char sim_schedule_doc[] = "Schedule a python function.\n\
00792    \n\
00793    Calling syntax:\n\
00794       sim.schedule(t,f)\n\
00795    \n\
00796    This schedules the function f to be run at simulation time t.\n\
00797    For example:\n\
00798    \n\
00799    def f():\n\
00800       print sim.time()\n\
00801    sim.schedule(10,f)\n\
00802    sim.run()\n\
00803    \n\
00804    would produce the output:\n\
00805    \n\
00806    [10L, 0L]\n\
00807    \n\
00808    f may also be any callable python object.\n";
00809 
00819 static PyObject*
00820 sim_schedule( PyObject* self, PyObject* args )
00821 {
00822    Entity::ArgList alist;
00823    try
00824    {
00825       build_arglist( alist, args );
00826       if ( 2 != alist.size() )
00827       {
00828          std::cerr << "sim.schedule() expected 2 arguments, "
00829                    << alist.size() << " provided"
00830                    << std::endl;
00831          throw SimulationException( __FILE__ , __LINE__ );
00832       }
00833       Time t = parse_time( alist[0] );
00834       InterpreterItem f = alist[1];
00835       if ( ! PyCallable_Check(f) )
00836       {
00837          throw SimulationException( __FILE__ , __LINE__ );
00838       }
00839       Clock::schedule( new InterpreterEvent( &iEntity, &iEntity, t, f ) );
00840       clean_arglist( alist );
00841    }
00842    catch ( ... )
00843    {
00844       PyErr_SetString(PyExc_ValueError,
00845                       "Bad argument list passed to sim.schedule");
00846       clean_arglist(alist);
00847       return 0;
00848    }
00849    Py_INCREF(Py_None);
00850    return Py_None;
00851 }
00852 
00855 static PyMethodDef SimMethods[] =
00856 {
00857    {"time",sim_time,METH_NOARGS,sim_time_doc},
00858    {"timeAsDouble",sim_timeAsDouble,METH_NOARGS,sim_timeAsDouble_doc},
00859    {"run",sim_run,METH_NOARGS,sim_run_doc},
00860    {"stopAt",sim_stopAt,METH_VARARGS,sim_stopAt_doc},
00861    {"collect",sim_collect,METH_NOARGS,sim_collect_doc},
00862    {"seed",sim_seed,METH_VARARGS,sim_seed_doc},
00863    {"random",sim_random,METH_NOARGS,sim_random_doc},
00864    {"args",sim_get_args,METH_VARARGS,sim_get_args_doc},
00865    {"schedule",sim_schedule,METH_VARARGS,sim_schedule_doc},
00866    {0,0,0,0} // sentinel
00867 };
00868 
00871 static char SimDoc[] = "Simulator namespace.\n\
00872    \n\
00873    The sim module, which is imported into simlpy by default,\n\
00874    contains the classes and methods needed for basic simulation\n\
00875    control.  Specific functional simulator elements are added\n\
00876    by importing additional modules, such as pydtn, into simlpy.\n";
00877 
00879 
00880 
00881 //==============================================================
00884 
00888 PyMODINIT_FUNC
00889 initsim()
00890 {
00891    sim_PySimEntity.tp_new = sim_entity;
00892    if ( PyType_Ready(&sim_PySimEntity) < 0 )
00893    {
00894       return;
00895    }
00896 
00897    PyObject* m = Py_InitModule3("sim",SimMethods,SimDoc);
00898    Py_INCREF(&sim_PySimEntity);
00899    PyModule_AddObject(m, "Entity", (PyObject*)&sim_PySimEntity);
00900    if ( PyRun_SimpleString("import sim") < 0 )
00901    {
00902       std::cerr << "Error importing sim\n"
00903                 << "exiting" << std::endl;
00904       ::exit(1);
00905    }
00906 }
00907 
00909 
00910 
00911 //==============================================================
00914 
00915 // Python-specific version of hook
00916 void
00917 add_entity( Entity* e )
00918 {
00919    try
00920    {
00921       Registry::insert( e );
00922    }
00923    catch ( ... )
00924    {
00925       std::cerr << "failed to register an Entity" << std::endl;
00926    }
00927 }
00928 
00929 unsigned int
00930 num_entities()
00931 {
00932    return entityList.size();
00933 }
00934 
00935 InterpreterItem
00936 get_entity(unsigned int i)
00937 {
00938    if ( i >= entityList.size() )
00939    {
00940       throw SimulationException( __FILE__ , __LINE__ );
00941    }
00942    return (PyObject*)(entityList[i]);
00943 }
00944 
00945 // Python-specific version of hook
00946 ItemWrapper
00947 resolve_symbol( InterpreterItem e )
00948 {
00949    ItemWrapper retval;
00950    if ( ! PyObject_TypeCheck(e,&sim_PySimEntity) )
00951    {
00952       PyErr_SetString(PyExc_TypeError,"expected a sim.Entity");
00953       throw SimulationException( __FILE__ , __LINE__ );
00954    }
00955    unsigned int id = ((PySimEntity*)e)->id;
00956    if ( id >= entityList.size() )
00957    {
00958       PyErr_SetString(PyExc_IndexError,"unknown sim.Entity");
00959       throw SimulationException( __FILE__ , __LINE__ );
00960    }
00961    PySimEntity* pse = entityList[id];
00962    if ( 0 == pse )
00963    {
00964       PyErr_SetString(PyExc_KeyError,"unknown sim.Entity");
00965       throw SimulationException( __FILE__ , __LINE__ );
00966    }
00967    retval.type = PyString_AsString(pse->type);
00968    retval.id = e;
00969    retval.item = pse->e;
00970    return retval;
00971 }
00972 
00973 long int
00974 parse_long( const InterpreterItem l )
00975 {
00976    long int retval = 0;
00977    if ( PyLong_Check( l ) )
00978    {
00979       retval = PyLong_AsLong(l);
00980    }
00981    else if ( PyInt_Check( l ) )
00982    {
00983       retval = PyInt_AsLong(l);
00984    }
00985    else
00986    {
00987       PyErr_SetString(PyExc_TypeError,"an integer was expected");
00988       throw SimulationException( __FILE__ , __LINE__ );
00989    }
00990    return retval;
00991 }
00992 
00993 double
00994 parse_double( const InterpreterItem d )
00995 {
00996    double retval = 0;
00997    if ( PyFloat_Check(d) )
00998    {
00999       retval = PyFloat_AsDouble(d);
01000    }
01001    else if ( PyLong_Check(d) )
01002    {
01003       retval = PyLong_AsDouble(d);
01004    }
01005    else if ( PyInt_Check(d) )
01006    {
01007       retval = PyInt_AsLong(d); // not ideal, but the best we can do
01008    }
01009    else
01010    {
01011       PyErr_SetString(PyExc_TypeError,"a real number was expected");
01012       throw SimulationException( __FILE__ , __LINE__ );
01013    }
01014    return retval;
01015 }
01016 
01017 std::string
01018 parse_string( const InterpreterItem s )
01019 {
01020    if ( ! PyString_Check( s ) )
01021    {
01022       PyErr_SetString(PyExc_TypeError,"a string was expected");
01023       throw SimulationException( __FILE__ , __LINE__ );
01024    }
01025    // This is a bit more complicated than it might seem like it should
01026    // be.  The reason for this is that we have to consider cases where
01027    // there are meaninful NULs in the data.
01028    const char* sbuf = PyString_AsString(s);
01029    std::string retval;
01030    for ( int i = 0 ; i < PyString_Size(s) ; ++i )
01031    {
01032       retval += sbuf[i];
01033    }
01034    return retval;
01035 }
01036 
01037 // Python-specific version of hook
01038 Time
01039 parse_time( const InterpreterItem t )
01040 {
01041    long sec=0;
01042    long usec=0;
01043 
01044    // Try to interpret it as a list, first.
01045    if ( PyList_Check( t ) )
01046    {
01047       int n = PyList_Size( t );
01048       if ( n < 2 )
01049       {
01050          PyErr_SetString(PyExc_TypeError,"a list [s,us] was expected");
01051          throw SimulationException( __FILE__ , __LINE__ );
01052       }
01053 
01054       sec = parse_long( PyList_GetItem( t, 0 ) );
01055       usec = parse_long( PyList_GetItem( t, 1 ) );
01056    }
01057    else
01058    {
01059       // Next, try as an integer or long int.
01060       try
01061       {
01062          sec = parse_long(t);
01063       }
01064       catch(...)
01065       {
01066          // Failing that, try as a double.
01067          PyErr_Clear(); // cancel the previous exception
01068          try
01069          {
01070             double d = parse_double(t);
01071             sec = (long int) ::floor(d);
01072             usec = (long int) ::floor( Time::MILLION * (d-sec) );
01073          }
01074          catch(...)
01075          {
01076             PyErr_SetString(PyExc_TypeError,
01077                             "could not interpret value as a time");
01078             throw SimulationException( __FILE__ , __LINE__ );
01079          }
01080       }
01081    }
01082    return Time(sec,usec);
01083 }
01084 
01085 InterpreterItem
01086 construct_string( const std::string& s )
01087 {
01088    return PyString_FromStringAndSize( s.data(), s.length() );
01089 }
01090 
01091 InterpreterItem
01092 construct_double( double d )
01093 {
01094    return PyFloat_FromDouble(d);
01095 }
01096 
01097 InterpreterItem
01098 construct_long( long int l )
01099 {
01100    return PyLong_FromLong(l);
01101 }
01102 
01103 InterpreterItem
01104 construct_ulong( unsigned long int l )
01105 {
01106    return PyLong_FromUnsignedLong(l);
01107 }
01108 
01109 InterpreterItem construct_list( Entity::ArgList& alist )
01110 {
01111    PyObject* p = PyList_New(0);
01112    if ( 0 == p )
01113    {
01114       return 0;
01115    }
01116    Entity::ArgList::iterator itr = alist.begin();
01117    Entity::ArgList::iterator end = alist.end();
01118    for ( ; itr != end ; ++itr )
01119    {
01120       // Takes over reference.
01121       PyList_Append( p, *itr );
01122    }
01123    return p;
01124 }
01125 
01126 InterpreterItem construct_boolean( bool b )
01127 {
01128    return PyBool_FromLong(b);
01129 }
01130 
01131 InterpreterItem construct_time( const Time& t )
01132 {
01133    PyObject* p = PyList_New(0);
01134    if ( 0 == p )
01135    {
01136       return 0;
01137    }
01138    PyList_Append( p, PyLong_FromLong(t.tv.tv_sec) );
01139    PyList_Append( p, PyLong_FromLong(t.tv.tv_usec) );
01140    return p;
01141 }
01142 
01144 static unsigned char vlevel = 0;
01145 
01146 unsigned char
01147 verbosity()
01148 {
01149    return vlevel;
01150 }
01151 
01152 PyObject*
01153 export_entity( Entity* e )
01154 {
01155    if ( 0 == e ) return 0;
01156 
01157    // Create the PyObject*
01158    PySimEntity* self =
01159       (PySimEntity*)sim_PySimEntity.tp_alloc(&sim_PySimEntity,0);
01160    if ( 0 == self )
01161    {
01162       std::cerr << "Could not allocate a new sim.Entity" << std::endl;
01163       return 0;
01164    }
01165    self->type = PyString_FromString( e->identifier().c_str() );
01166    if ( 0 == self->type )
01167    {
01168       Py_DECREF(self);
01169       return 0;
01170    }
01171    self->e = e;
01172    self->id = entityList.size();
01173    entityList.push_back(self);
01174    // Increment the reference count twice: once for the object returned
01175    // and once for entityList.
01176    Py_INCREF(self);
01177    Py_INCREF(self);
01178 
01179    return (PyObject*)self;
01180 }
01181 
01183 
01184 
01185 //==============================================================
01186 
01188 void
01189 cleanup()
01190 {
01191    SimEntityVec::iterator itr = entityList.begin();
01192    SimEntityVec::iterator end = entityList.end();
01193    for ( ; itr != end ; ++itr )
01194    {
01195       if ( 0 != (*itr)->e )
01196       {
01197          (*itr)->e->finalize();
01198          delete (*itr)->e;
01199       }
01200       PySimEntity_dealloc( *itr );
01201    }
01202    Registry::clean();
01203 }
01204 
01208 int main(int argc, char** argv)
01209 {
01210    typedef std::queue< std::string > StringQ;
01211 
01212    // Parse command-line flags using getopt(3).
01213    std::string python_path;
01214    StringQ module_path;
01215    StringQ import_files;
01216    Entity::ArgList alist;
01217    int arg = 0;
01218    while ( -1 != arg )
01219    {
01220       arg = getopt(argc,argv,"p:i:L:va:");
01221       switch(arg)
01222       {
01223          case 'p' :
01224             python_path = optarg;
01225             break;
01226          case 'i' :
01227             import_files.push( optarg );
01228             break;
01229          case 'L' :
01230             module_path.push( optarg );
01231             break;
01232          case 'v' :
01233             ++vlevel;
01234             break;
01235          case 'a' :
01236             alist.push_back( construct_string(optarg) );
01237             break;
01238          case ':' :
01239          case '?' :
01240             std::cerr << "Add usage string!!" << std::endl;
01241             ::exit(1);
01242       }
01243    }
01244 
01245    sim_args = construct_list(alist);
01246 
01247    // What remains (if anything) should be Python script files.
01248    StringQ script_files;
01249    for ( int i = optind ; i < argc ; ++i )
01250    {
01251       script_files.push( argv[i] );
01252    }
01253 
01254    // Start the interpreter, feeding it the above files.
01255    if ( python_path.length() > 0 )
01256    {
01257       Py_SetProgramName((char*)python_path.c_str());
01258    }
01259    else
01260    {
01261       // Stupid hack -- The embedded interpreter doesn't know where the
01262       // executable is, even though it's been compiled and linked with
01263       // files from the appropriate directory.  Consequently, sys.path
01264       // is completely buggered for non-standard python installation
01265       // directories.
01266       Py_SetProgramName((char*)PYEXE);
01267    }
01268    Py_AtExit( cleanup );
01269    Py_Initialize();
01270    PySys_SetArgv(argc,argv);
01271 
01272    // simulator configuration
01273    initsim();
01274 
01275    // HACK: we need to set up the module search path
01276    PyRun_SimpleString("import sys");
01277    PyRun_SimpleString("sys.path.append('"SHLIB_DIR"')");
01278    PyRun_SimpleString("sys.path.append('.')");
01279    while ( ! module_path.empty() )
01280    {
01281       std::string dir = module_path.front();
01282       module_path.pop();
01283       std::string cmd = "sys.path.append('";
01284       cmd += dir;
01285       cmd += "')";
01286       if ( PyRun_SimpleString((char*)cmd.c_str()) < 0 )
01287       {
01288          std::cerr << "Error adding " << dir << " to the path\n"
01289                    << "exiting" << std::endl;
01290          ::exit(1);
01291       }
01292    }
01293 
01294    // the modules to import
01295    while ( ! import_files.empty() )
01296    {
01297       std::string mod = import_files.front();
01298       import_files.pop();
01299       std::string cmd = "import ";
01300       cmd += mod;
01301       if ( PyRun_SimpleString((char*)cmd.c_str()) < 0 )
01302       {
01303          std::cerr << "Error importing " << mod << "\n"
01304                    << "exiting" << std::endl;
01305          ::exit(1);
01306       }
01307    }
01308 
01309    // the scripts to run
01310    while ( ! script_files.empty() )
01311    {
01312       std::string scr = script_files.front();
01313       script_files.pop();
01314       FILE* fp = fopen(scr.c_str(),"r");
01315       if ( 0 == fp )
01316       {
01317          std::cerr << "Could not open script file " << scr << "\n"
01318                    << "exiting" << std::endl;
01319          ::exit(1);
01320       }
01321       if ( PyRun_SimpleFile(fp,(char*)(scr.c_str())) < 0 )
01322       {
01323          std::cerr << "Error running file " << scr << "\n"
01324                    << "exiting" << std::endl;
01325          ::exit(1);
01326       }
01327       fclose(fp);
01328    }
01329 
01330    // If we've gotten this far, we're a candidate for successful completion.
01331    int retval = 0;
01332 
01333    // If we're still active when all files have been processed, drop
01334    // into the interactive interpreter.
01335    if ( Py_IsInitialized() )
01336    {
01337       PyRun_SimpleString("import readline");
01338       retval = PyRun_InteractiveLoop(stdin,"<stdin>");
01339    }
01340 
01341    // At this point, we might as well explicitly clean up allocated memory.
01342    Py_Exit( retval );
01343 
01344    return retval;
01345 }
01346 

Generated on Mon Mar 24 11:15:46 2008 for Pydtn Simulator by  doxygen 1.5.4