Developing for Chimera¶
Introduction¶
Chimera uses a client/server model coupled with remote procedure call (RPC) system. Chimera defines all its entities in terms of objects.
To be a valid Chimera object a class must extend
ChimeraObject
class. ChimeraObject
class implements
ILifeCycle
interface which defines basic methods every object
must have to be started and stopped. ChimeraObject
provides a
basic implementation of a control loop, a common structure in control
programs.
By itself, a Chimera object is just a static bunch of code that
inherits from a specific class. To be useful, every
ChimeraObject
must have a Manager
to manage its life
cycle.
Note
We call it instrument, controller or driver purely from a semantic
point of view, as they are equal from a code point of view, there is
no different base class for different kinds of objects. Every
instrument, controller or driver extends ChimeraObject
. More
specifically, it must implement ILifeCycle
, and
ChimeraObject
provides us with a basic implementation.
The Manager
class is responsible for object initialization, life
cycle (start, stop) and proxy creation. Manager
is also our
server in the client/server model. It’s a server of objects. We can
think of Manager
as a pool of objects available to be used.
As we need RPC support for every object and want to make the system easy to use and write, we use a Proxy class that handles all the networking for us. This way, you don’t have to write networking code on your objects, just the real action, Proxy and friends add networking for you.
Before describing Manager
‘s responsibilities in more detail,
let’s describe all features we can have in a Chimera object.
Chimera objects¶
A Chimera object, as already said, is a normal Python class which extends from
a specific base class, ChimeraObject
, the simplest
ChimeraObject
is the following.
from chimera.core.chimeraobject import ChimeraObject
class Simplest (ChimeraObject):
def __init__ (self):
ChimeraObject.__init__(self)
this object has no methods, configuration or events, so it’s the simplest and dumbest possible object.
As Python doesn’t call base constructors per se, you need to call the
base constructor from Simplest constructor (__init__()
method).
Chimera uses the concept of Location
all over the code. A
Location is much like an URL, but without a scheme. Locations
identify specific class instances running somewhere. The basic
format is the following:
[host:port]/Classname/instance_name[?param1=value1,...]
host and port, optional fields, tell Chimera where to look for
this particular object. Classname is the class name of the object
and instance_name the name given to a specific instance running on
host:port. When you add objects to Manager
, you must specify
a name. Also, you can pass configuration parameters as comma separated
param=value pairs.
Let’s write down a class that uses this and see how to actually use this from Chimera.
from chimera.core.chimeraobject import ChimeraObject
class Example1 (ChimeraObject):
__config__ = {"param1": "a string parameter"}
def __init__ (self):
ChimeraObject.__init__(self)
def __start__ (self):
self.doSomething("test argument")
def doSomething (self, arg):
self.log.warning("Hi, I'm doing something.")
self.log.warning("My arg=%s" % arg)
self.log.warning("My param1=%s" % self["param1"])
This example requires some explanations, but before, let’s run it
using chimera script. chimera script is a script
to initialize Manager
and add objects either from Locations
given on command line or from a configuration file.
To follow Chimera conventions, a file with a class named
Example1
must be saved to a file name example1.py
to
allow Chimera ClassLoader to find it. We may simplify this in the future.
You can save this file anywhere on your system, let’s suppose you saved it on your $HOME directory.
To run it, call chimera this way:
$ chimera -I $HOME -i /Example1/example
You’ll see something like this:
[date] WARNING chimera.example1 (example1) example1.py:15 Hi, I'm doing something.
[date] WARNING chimera.example1 (example1) example1.py:16 My arg=test argument
[date] WARNING chimera.example1 (example1) example1.py:17 My param1=a string parameter
You should use Ctrl+C (SIGINT) to stop chimera.
The -I on chimera tells Chimera where to look for instruments, this case to look in your $HOME directory (you can use any directory there, ‘.’ for example). Then to -i we pass a valid Chimera Location. From the Location, Chimera knows that you want to create an instance of Example1 class and call this instance ‘example’.
You can also pass configuration parameters right on the Location given in the command line. Use:
$ chimera -I $HOME -i /Example1/example?param1="Now for something different"
A few points need explanation on Example1
:
1.
__config__
is a class attribute (class field in some circles) where you should pass a Python dictionary with any parameter you like to add to your object. Chimera uses the value you pass in as default value and also uses the type of it to do some type checking for you. Look at src/chimera/core/config.py for valid types.2.
__start__()
method. This method is fromILifeCycle
interface,ChimeraObject
implementation just does nothing, here we use it to call a specific method on object initialization.Manager
first call__init__()
to create an instance, configure this instance passing any parameter you gave on command line, then call__start__()
and when system is shutting down call__stop__()
.3.
log
.ChimeraObject
implementation give a log attribute (instance field, in other circles) to every class, you can use this to log messages to default Chimera log system. It’s a normal Python’slogging
logger, so consult logging for more information.4.
self["param1"]
. You define your object parameters using__config__
dict, but to access the actual value, you use the current object (self) as dictionary to access values from it. Thus,self["param1"]
treat self as a dict and get key param1 from it. For most purposes, self is a dict and normal dict. You can also set things, with normalself["param1"] = "value1"
.
When you use chimera script, a Manager
is created
for you, but you can do it by yourself to learn how things work in
Chimera. The following example is based on server.py
.
from chimera.core.manager import Manager
manager = Manager(host='localhost', port=8000)
manager.addLocation("/Example1/example", start=True)
manager.wait()
Suppose you save it to server.py
in the same directory where
you put example1.py
(this is a not a restriction, just to make
things easier).
$ python server.py
You’ll see exactly the same as running chimera.
But, as said in the first paragraph of this document Chimera is
client/server, server.py
shows how to create a server, let’s
see how to use it in a client.
from chimera.core.manager import Manager
manager = Manager()
example = manager.getProxy("localhost:8000/Example1/example")
example.doSomething("client argument")
Save it to client.py
. First run, server.py
as
explained above and then run client.py
.
$ python client.py
You will see something like this:
[date] WARNING chimera.example1 (example1) example1.py:15 Hi, I'm doing something.
[date] WARNING chimera.example1 (example1) example1.py:16 My arg=test argument
[date] WARNING chimera.example1 (example1) example1.py:17 My param1=a string parameter
[date] WARNING chimera.example1 (example1) example1.py:15 Hi, I'm doing something.
[date] WARNING chimera.example1 (example1) example1.py:16 My arg=client argument
[date] WARNING chimera.example1 (example1) example1.py:17 My param1=a string parameter
The first three lines are from __start__()
calling
doSomething()
, and later three from our client calling it again.
In client.py
, you see we create a normal Manager
,
just like in server.py, but we only use this Manager
to get
access to Example1
running on other Manager
(localhost:8000).
Manager.getProxy()
returns a Proxy
object for the
specifies Location. For all purposes this Proxy
class acts
like the original object, so you can call any method just like you
would with the original object.
Plugin development¶
After trying to find inside the chimera core package, chimera tries to find controllers and instruments on packages with names starting with chimera_. This opens chimera to be customizable with third-party plugins of all kinds.
To facilitate the development of those plugins, we created a plugin template which can be forked and changed to born a new plugin. Take a look on our chimera-template plugin and, if there is any doubt, don’t hesitate to contact us by opening an issue on github or sending an e-mail to our mailing list