================== The cmdmod wrapper ================== cmdmod is a wrapper that simplifies the development of Pyrame modules by mutualizing the network server and client, the XML and :doc:`Pyrame protocol ` parsing and the control of public/private functions among others. Figure 1 summarizes the inner workings of cmdmod. .. figure:: cmdmod.doc.png :align: center :scale: 35% Figure 1: Example of module Y interacting with modules X and Z. Module Y is run inside :doc:`cmdmod ` cmdmod's usage is: .. code-block:: sh cmdmod config_file [-c cmod_ip] On the following the :ref:`config_file ` and the :ref:`cmod functionality ` are described. .. _cmdmod_config_file: The XML config_file =================== *config_file* is an XML file with a main `` tag describing a number of parameters of the module. The required parameters are the Python implementation file and the TCP listening port : .. code-block:: xml /opt/pyrame/cmd_test.py 9212 Optionally, a ports file (via the ** tag) or functions (via the ** tag) can be included. The ports file is usually */opt/pyrame/ports.txt*, which includes all the stock Pyrame distribution ports. When the ports file is indicated, a non-numeric port can be used: .. code-block:: xml /opt/pyrame/cmd_test.py /opt/pyrame/ports.txt CMD_TEST Regarding the functions, depending on the functionality of the module and its comunication needs with others, a list of *script* or *host* functions can be included: .. code-block:: xml /opt/pyrame/cmd_test.py CMD_TEST /opt/pyrame/ports.txt test_test localhost VARMOD_PORT Script functions ---------------- The *script* functions are those that exist in the module and are intended to be public and thus callable from other modules. The ** tag must include a public *name* and be of *type=script*. Inside the ** tag a ** tag must be included with the name of function on the Python implementation file. In most cases both will coincide for simplicity. Host functions -------------- The *host* functions are those that are implemented in other Pyrame modules and that the module might need to call at some point of its execution. The ** tag must include the public *name* of the function on the external module. Inside the ** tag, ** and ** tags must be included with the hostname (or IP) and the port of the external module. The port can be of text-type, if a ** ports file is given and the desired port name is included on it. The submod API ============== cmdmod provides plenty of functions to its modules. They are presented here by theme: Basic functions --------------- .. function:: submod.execcmd(function@module[,parameter1[,...]]) It allows the code to call an external Pyrame function on another module. The first parameter is the function name suffixed with the module name and the rest is the parameters. Once the Pyrame command has been sent to the external module, the function waits for a response indefinetely. The response can be of any size within the system's memory constrains. The return value is a tuple of return numerical code and return string. .. function:: submod_exit(exit_code) This function make cmdmod to exit gently and emitting the exit_code .. function:: submod_getport(module_name) Return the numerical port value of a Pyrame module known by its name .. function:: submod_setmyname(newname) Allow a module to rename itself. This operation is typically done during init function. Environment ----------- Cmdmod provides an environment to its modules. It is a storage where the module can store simple values (string, integer or flotting point numbers). The values are indexed by a name and a namespace. This allow to create objects (the namespace) and to have multiple variable inside (the names) This environment is backuped on the disk at any modification. This way, if a module crash, it can be relaunched with its previous environment and thus the system recover from this very severe problem. The environment is automatically coupled to cmod which allow to syncrhonize the values of the module and the value of the xml configuration file. The programmer does not need anymore to register the object in cmod nor update the value of its parameters. .. function:: submod_setvar(ns,name,value,type) store a variable in namespace "ns" with the name "name" with value "value" and of type "type". This type has to be numerical (0 for string, 1 for int and 2 for float) .. function:: submod_getvar(ns,name) get the value of a variable in a namespace .. function:: submod_gettype(ns,name) get the type of a variable in a namespace .. function:: submod_delvar(ns,name) delete a variable in a namespace .. function:: submod_newns(ns) create a namespace .. function:: submod_checkns(ns) check if a namespace exists .. function:: submod_delns(ns) delete a namespace .. function:: submod_dumpns(ns) dump the values of all variables of a namespace .. function:: submod_listns() give the list of all available namespace Dispatcher ---------- Cmdmod includes a data dispatching system allowing to distribute online data along all other modules. The data are made of blocks of events. The module store these blocks in a circular buffer and send them to any requesting module. It can also describes its own data through a schema. .. function:: submod_init_dispatcher(nb_blocks,name,schema_str,port) This function initialize the data dispatcher. - nb_blocks is the number of available block slots in the circular buffer - name is the name of the data source - schema_str is a string describing the data format in saf #TODOREF - port is the numerical value of the listening port. If undef, it is allocated automatically by the system Note that only one dispatcher is available by module. Thus this function must be called only once. .. function:: submod_flush_dispatcher() Remove any data from the circular buffer .. function:: submod_new_block(block_id) Create a new block with block_id as identifier and return a handler for it .. function:: submod_set_field_block(block,field_name,value) Fill a block with a data - block is the block identifier returned by submod_new_block - field_name is the name of a field as described in the schema - value is the value of the field .. function:: submod_new_event(block) Create an event in the block and return a handler for it .. function:: submod_set_field_event(event,field_name,value) Fill an event with a data - event is the event identifier returned by submod_new_event - field_name is the name of a field as described in the schema - value is the value of the field .. function:: submod_finalize_block(block) Finalize a block: the block is checked for verifying that all fields has been properly filled and then add the block to the circular buffer. .. function:: submod_set_chans(schema) Change the schema of the dispatcher Asynchronous execution ---------------------- To avoid all risk of deadlock, the Pyrame architecture allow a module to execute only one action at a time. In some very special case, it is necessary to execute asynchronous action. These two function allow that but be careful when using them because a deadlock can appears as soon as a module calls another module that calls him in return. .. function:: submod_bgcontext() obtain a background context. Necessary to go to asynchronous mode .. function:: submod_sendres(context,retcode,retstr) send the result of the execution when the asynchronous part of the function is executed. - context is the background context obtained previoulsy by submod_bg_context - retcode is the numerical return code - retstr is the return message Note that the caller has been remained waiting during the asynchronous part. Only another module can ask for more function. Here is an example code in Python for using asynchronous calls .. code-block:: python def asyncwait_t(bg_context,ptime): print("waiting for %s seconds"%(ptime)) time.sleep(float(ptime)) print("sleep is finished") submod_sendres(bg_context,1,"ok") #pyrapi:asyncwait_test:time def asyncwait_test(ptime): retcode,res=submod_bgcontext() t=threading.Thread(target=asyncwait_t,args=(res,ptime)) t.start() return KEEPOPEN,"ok" When the command asyncwait_test is called, a thread is launched and the Keepopen code is sent. Then, the module is ready to execute other commands but the caller of the asyncwait_test function waits. In the thread, the action goes on and when finished the sendres define the result of the call. This retcode and retstr are then sent to the original caller. .. _cmdmod_cmod: CMOD functionality ================== cmdmod allows to extend further the *host* functions functionality provided by the XML by using the :doc:`cmod ` module. It is therefore only used when calling external functions via *submod.execcmd*. It is enabled by using the *-c cmod_ip* command-line argument, with *cmod_ip* being the IP or hostname where cmod runs. When using cmod, the hostname of the module receiving the function call is determined in realtime (and cached for subsequent calls) based on the first parameter of call, which must be the device_id assigned by cmod. The module being called must coincide in name with substring of the function after the last "_" character. Example: A module called *chip* is present in two network nodes and each manages different physical devices of the same type. The module *chip* registers their devices on cmod with type *chip*. The module implements a function *init_chip(dev_id)*. On this situation the *host* function scheme of the XML *config_file* is no longer valid, as the same function *init_chip* exists on two network nodes, and the selection of it depends on the first parameter of the function call. When a Python code running on cmdmod wants to call *init_chip(3)*, cmdmod will detect that this function is not present on the XML *config_file* and, if the cmod functionality has been enabled, it will try to resolve from cmod the network node to which address that function call, based on the first parameter (3) and the type of device ("chip"). The type of device is extracted from the function name, as the substring after the last "_". Note that this forbids this functionality to work with modules that have "_" characters on their name.