Welcome to DRAMA Python Interface’s documentation!

This is the documentation DRAMA interface to Python, via the DRAMA2 API. It allows you to create python scripts and GUIs in Python which will communicate with DRAMA Tasks.

I’m afraid the layout of this documentation is rather poor - it is automatically generated from the source but there are some restrictions on what I can do - I hope to work out how to improve it over time.

The interface is generated using the SWIG tool. The table belows shows the main entry point classes and procedures, but there are various other support classes listed in the Code Documentation and Index.

Having started DRAMA in the normal way, the DPYTHON_DIR is put in PYTHONPATH and you should be able to import dpython to access these classes.

Class Name

Description

dpython.Task

Implements a DRAMA task.

dpython.Path

Implements a Path object used to talk to other tasks.

dpython.AId

Provides access to DRAMA’s SDS objects (A for argument).

dpython.pmonitor

Provides the ability to monitor parameters in other tasks.

dpython.SdsToDict

Convert an SDS structure to a dictionary.

dpython.MonitorToDict

A subclass of pmonitor which puts parameter values in a dictionary.

The source code library includes the file pythontest.py, which is a general test program. The examples below are based on that file.

Here is how you might define a class to run the DRAMA message loop in a thread:

 1class dramaTask (threading.Thread):
 2    def __init__(self):
 3        threading.Thread.__init__(self)
 4    def run(self):
 5        self.task = dpython.Task("AAA")
 6
 7        self.task.AddExitAction()
 8        self.task.RunDrama()
 9    def GetTask(self):
10        return dpython.TaskWeakPtr(self.task)
11    def SignalExit(self):
12        self.task.SignalDramaToExit(0)

And then to actually start the task, use:

1dramaThread = dramaTask()
2dramaThread.start()
3time.sleep(1)

Note that after executing the start() method, you typically wait a second for the DRAMA task to actually start (really, to force a thread context switch). Otherwise the DRAMA task may not be running when you try to do something.

The following show how you might load and run the task TICKER, from the file ticker in the directory $DITS_DEV (presuming the DRAMA networking is running):

1myPath = dpython.Path(dramaThread.GetTask(), "TICKER", "", "DITS_DEV:ticker")
2myPath.SetBuffers(1600,1600);
3myPath.GetPath()

To send an obey message to TICKER using this path, you would do:

1myPath.Obey("TICK")

And this shows how to create a command structure (might use for an Obey as well) and then to use it to set a parameter value:

1a = dpython.AId.CreateArgStruct()
2a.Puti("Argument1", 10)
3myPath.SetParam("PARAM1", a)

Here a get followed by various AId (SDS) calls to examine the result:

 1b = myPath.GetParam("PARAM1")
 2b.List()
 3
 4name = b.GetName()
 5code = b.Code()
 6if code == "SDS_STRUCT":
 7    item = b.Find("PARAM1")
 8    name = item.GetName()
 9    code = item.Code()
10    dims = item.GetNumArrayDims()
11    value = item.toString()

Here we create an SDS structure, list it and then convert it to a dictionary to demostrate the required calls:

 1a = dpython.AId.CreateArgStruct()
 2a.Putc("CharItem", 3);
 3a.Puts("ShortItem", 4);
 4a.Putus("UShortItem", 5);
 5a.Puti("INT32Item", 6);
 6a.Putui("UINT32Item", 7);
 7a.Puti64("INT64Item", 8);
 8a.Putui64("UINT64Item", 9);
 9a.Putf("FloatItem", 10.1);
10a.Putd("DoubleItem", 11.2);
11a.PutStr("StringItem1", "12");
12
13# Have a look at what we have created (to stdout).
14a.List()
15
16#
17# We can convert things like this to python dictionaries.
18#
19print (dpython.SdsToDict(a))

Or reading an SDS structure from a file and converting it to a dictionary:

1b = dpython.AId.Read("tict_pars.sds")
2print (dpython.SdsToDict(b))

Note that to cause the DRAMA task thread to exit, you need to execute:

1dramaThread.SignalExit()

DRAMA Parameter monitoring allows your task to be notified automatically if the value of a parameter in another task changes. It requires that, having created your DRAMA message reading thread, that you create and run another thread to do the monitoring. In the example below, I first create a class which will be used to implement thread. This class uses an object of type dpython.MonitorToDict, itself a sub-class of dpython.pmonitor, to do the monitoring. The MonitorToDict class is used to cause the parameter values to appear in a dictionary. You could create your own sub-class of pmonitor to have other things happen on the changes.:

 1class dramaMonitor (threading.Thread):
 2    # Constructor.  Save details to be used when thread starts.
 3    def __init__(self, task, path, parameters):
 4        threading.Thread.__init__(self)
 5        self.task = task.GetTask()
 6        self.param = parameters
 7        self.path  = path
 8        self.monitor = None
 9
10    # After the thread is started, this is run within the thread
11    #  to do the real work of thread.  We set up and run our monitor,
12    #  using a variable of type myMonitor (a sub-class of
13    #   dpython.pmonitor) to do the monitoring.
14    def run(self):
15        self.monitor = dpython.MonitorToDict(self.task, self.param)
16        self.monitor.RunMonitor(self.path)
17        print ("Dictionary contains:")
18        print (self.monitor.dict)
19    # Method used to cancel the monitor.
20    def cancel(self):
21        if self.monitor:
22            self.monitor.Cancel()

Once I have this class definitoin, I can ran the monitor like so:

 1# Create the monitor thread object.  Specifying parameter to monitor
 2#   Presumes the dramaThread and myPath objects from previous examples.
 3monitorThread = dramaMonitor(dramaThread, myPath,
 4                             dpython.StringVector(["PARAM1",
 5                                                   "PARAM2",
 6                                                   "PARAM3",
 7                                                   "PARAM4",
 8                                                   "STRUCT_PARAM"]))
 9# And now start the monitor thread.
10monitorThread.start()

Parameter monitoring is particuarly usefull to allow a GUI to automatically update displayed items.

Contents:

Indices and tables

Example code

The full dpython test program is below. This can be found in the dpython ACMM sub-system.

  1# "@(#) $Id: ACMM:dpython/dpythontest.py,v 1.13 12-Jun-2020 14:26:46+10 aidanfarrell $"
  2#
  3#  Basic dpython test script.
  4#
  5# This script is meant to exercise most of the features of dpython.
  6#
  7# It creates a DRAMA task named AAA and runs it in a thread. It creates
  8#  two other threads which work whilst the DRAMA task is running (but
  9#  don't do anything real - they just demostrate when you can do)
 10#
 11# It presumes the TICKER program (DRAMA Task) is running (at the
 12# moment, a future version may load it)
 13#
 14# Various messages are sent to the TICKER task before it is told to exit, and
 15# then the DRAMA thread is exited. We then wait for the other threads to exit.
 16#
 17
 18
 19# Script to test dpython
 20
 21# Load the DRAMA code.
 22import dpython
 23# And the threading interfaces
 24import threading
 25
 26# We need to access time of day.
 27import time
 28
 29 
 30exitFlag = 0
 31
 32#
 33# This class is used to implement the two worker threads.
 34#
 35#   These are just used to demostrate that you can have
 36#   other things runnings whilst the DRAMA task is working.
 37#
 38class myThread (threading.Thread):
 39    def __init__(self, threadID, name, counter):
 40        threading.Thread.__init__(self)
 41        self.threadID = threadID
 42        self.name = name
 43        self.counter = counter
 44    def run(self):
 45        print ("Starting " + self.name)
 46        print_time(self.name, 1, self.counter)
 47        print ("Exiting " + self.name)
 48
 49#
 50# This class is used to implement the DRAMA message processing
 51# thread.
 52#
 53class dramaTask (threading.Thread):
 54    def __init__(self):
 55        threading.Thread.__init__(self)
 56    def run(self):
 57        """ Make ourselves a DRAMA task named AAA and add an EXIT action.
 58        Run the DRAMA message processing loop
 59        """
 60        self.task = dpython.Task("AAA")
 61        self.task.AddExitAction()
 62        print ("Starting DRAMA task")
 63        self.task.RunDrama()
 64        print ("DRAMA task shutdown")
 65    def GetTask(self):
 66        """ References to the underlying DRAMA task are needed at times, return it"""
 67        return dpython.TaskWeakPtr(self.task)
 68    def SignalExit(self):
 69        """ Tell the DRAMA messaging thread to exit """
 70        self.task.SignalDramaToExit(0)
 71
 72
 73#
 74#
 75# This class is used to implement a thread which
 76#  sends a DRAMA Parameter monitor message and
 77#  processes the replies.
 78#
 79# The results of the monitor are put in a dictionary,
 80#  which can be accessed from the monitor.dict item.
 81#
 82class dramaMonitor (threading.Thread):
 83    def __init__(self, task, path, parameters):
 84        threading.Thread.__init__(self)
 85        self.task = task.GetTask()
 86        self.param = parameters
 87        self.path  = path
 88        self.monitor = None
 89        
 90    # After the thread is started, this is run within the thread
 91    #  to do the real work of thread.  We set up and run our monitor,
 92    #  using a variable of type myMonitor (a sub-class of
 93    #   dpython.pmonitor) to do the monitoring.
 94    def run(self):
 95        print ("Starting dramaMonitor thread")
 96        #self.monitor = dpython.pmonitor(self.task, self.param)
 97        self.monitor = dpython.MonitorToDict(self.task, self.param)
 98        self.monitor.RunMonitor(self.path)
 99        print ("Ended dramaMonitor thread, dictionary contains:")
100        print (self.monitor.dict)
101    # Method used to cancel the monitor.
102    def cancel(self):
103        if self.monitor:
104            print("### Will cancel monitor")
105            self.monitor.Cancel()
106        else:
107            print("Monitor not running to can't cancel it")
108        
109        
110
111def print_time(threadName, delay, counter):
112    print ("Running thread:" + threadName + ", delay =" + str(delay) + ", count=" + str(counter))
113    while counter:
114        if exitFlag:
115            threadName.exit()
116        time.sleep(delay)
117        print ("" +threadName + " " + str(counter) + ", " + time.ctime(time.time()))
118        counter -= 1
119
120# Create new thread objects.
121thread1 = myThread(1, "Thread-1", 5)
122thread2 = myThread(2, "Thread-2", 7)
123dramaThread = dramaTask()
124
125# Start new Threads
126thread1.start()
127thread2.start()
128dramaThread.start()
129
130# So at this point, 4 threads are running - the thread running this
131# code, thread1 and thread2, which are worker threads, and the dramaThread
132# which is running the drama message processing loop.
133
134
135# This wait is needed to allow the DRAMA thread to start, otherwise
136#   things won't work, in particular, the DRAMA thread won't be properly
137#   started by the time we start using DRAMA below.
138time.sleep(1)
139
140
141
142
143# Create a path object to task to the TICKER task, and get the path.
144print("Creating DRAMA path object")
145myPath = dpython.Path(dramaThread.GetTask(), "TICKER", "", "DITS_DEV:ticker")
146print("Have new DRAMA path object");
147# DRAMA requires you to set buffer sizes before you set up the path.
148#   See the DRAMA documentation for details.
149myPath.SetBuffers(1600,1600);
150#
151# Now actually get the path - this is where we confirm the task TICKER
152#  is running and set up for commuicating with it.
153myPath.GetPath()
154
155
156#
157# Set up a parameter monitor.
158#
159monitorThread = dramaMonitor(dramaThread, myPath,
160                             dpython.StringVector(["PARAM1",
161                                                   "PARAM2",
162                                                   "PARAM3",
163                                                   "PARAM4",
164                                                   "STRUCT_PARAM"]))
165monitorThread.start()
166
167
168
169#
170#  The TICKER task has an action (command) named "TICK".  The basic
171#   DRAMA test is to send a set of these.  So loop whilst sending
172#   the commands.
173#
174#   Note - myPath.Obey() will block this thread until the TICK action
175#    is complete.  If you want this thread to do other things, you need
176#     to offload the "Obey" to another thread.
177#
178print("Sending 100 tick messages to the TICKER task")
179ticks = 100
180while (ticks):
181    myPath.Obey("TICK")
182    ticks -= 1
183
184#
185# Show how we can set a task parameter.  TICKER has a parameter named
186# PARAM1 which we set the value of.
187#
188# Also demostrates how to create an argument for a message (Via the dpython.Aid
189#  class).  All DRAMA command arguments (and reply arguments) are DRAMA
190#  SDS structures.  So here we are creating an SDS structure, a very simple
191#  one.
192#
193print("Sending SET message for PARAM1 to TICKER")
194a = dpython.AId.CreateArgStruct()
195a.Puti("Argument1", 10)
196myPath.SetParam("PARAM1", a)
197
198# Allow monitor update to occur and output - so that output
199#  occurs in a well defined order.
200time.sleep(1)
201#
202# Similarly for getting a parameter value.  We get the value back and
203#  in this case, just List the value to stdout.  You can of course get
204#  the value into your code.  The value you have gotten back is again
205#  an SDS structure, so you could get something of considerable complexity
206#  and size back (including large Image structures).
207#
208print("Sending GET message for PARAM1 to TICKER")
209b = myPath.GetParam("PARAM1")
210name = b.GetName()
211code = b.Code()
212print("Get Parameter result TopLevel:Name = ", name, ", Code = ", code)
213if code == "SDS_STRUCT":
214    item = b.Find("PARAM1")
215    name = item.GetName()
216    code = item.Code()
217    dims = item.GetNumArrayDims()
218    print("Get Parameter result Item    :Name = ", name, ", Code = ", code, ", NumArrayDims=", dims)
219    value = item.toString()
220    print("Get Parameter result Value:", value)
221
222
223# Get with exception handling to see how this work.
224try:
225    b = myPath.GetParam("PARAM1Y")
226except RuntimeError as e:
227    print("Runtime Error received getting PARAM1Y - expected, exception handling test")
228    print(e.args)
229
230
231
232# Cancel the monitor.
233monitorThread.cancel()
234# temp wait
235time.sleep(2)
236
237#
238# Shutdown the TICKER task by sending it an EXIT command.
239#
240print("Will shutdown TICKER tick")
241myPath.Obey("EXIT")
242
243#
244# Wait for it to go.  (It is not really clear we need to do this)
245#
246time.sleep(1)
247
248
249#
250# SignalExit() causes the task.RunDrama() method to return, and as
251#  a result this task stops processing DRAMA messages and the drama
252#  thread will exit.
253#
254dramaThread.SignalExit()
255
256#
257# Extra examples,
258#
259# Here, creating a more complex SDS structure.
260#
261print("Creating and listing an argument structure")
262a = dpython.AId.CreateArgStruct()
263a.Putc("CharItem", 3);
264a.Puts("ShortItem", 4);
265a.Putus("UShortItem", 5);
266a.Puti("INT32Item", 6);
267a.Putui("UINT32Item", 7);
268a.Puti64("INT64Item", 8);
269a.Putui64("UINT64Item", 9);
270a.Putf("FloatItem", 10.1);
271a.Putd("DoubleItem", 11.2);
272a.PutStr("StringItem1", "12");
273
274# Have a look at what we have created (to stdout).
275a.List()
276
277#
278# We can convert things like this to python dictionaries.
279#
280print (dpython.SdsToDict(a))
281
282#
283# We can also read SDS structures from files.
284#
285b = dpython.AId.Read("tict_pars.sds")
286
287print (dpython.SdsToDict(b))
288
289
290print ("Exiting Main Thread")