pycda and qcda - femanov/pycx4 GitHub Wiki

User's manual

general notes

There are CX main loop and Qt versions of extension, named pycda and qcda(q4cda/q5cda), wich provide almost the same API. There are few differences:

  • Import the one you need. Use import pycx4.pycda as cda for CX main loop based module or import pycx4.qcda as cda for Qt4/Qt5 versions. Modules q4cda/q5cda are binary therefor it's not possible to make them a single portable Qt module. Since we need to make apps compatible with both Qt versions there are pure python qcda submodule which looks which PyQt is imported or try to import any version, then load appropriate qXcda to qcda namespace.

  • pycda brings main_loop and break_ wrapper functions to control CX scheduler main loop, while with qcda you need to take it from pyqt.

  • pycda implements Timer and Signal classes - Qt-like ones for CX scheduler

  • Run application main loop (event loop):

for PyQt app:

    from PyQt5 import QtCore
    
    app = QtCore.QCoreApplication(sys.argv)
    (...your code here...)
    app.exec_()

for console app:

Need to rewrite following

   import pycx4.scheduler as sl
   (...your code here...)
   sl.main_loop()
  • Signals are pyqtSignal for Qt-version, and sl.Signal for console version. sl.Signal interface is like pyqtSignal one - connect, disconnect and emit with the same usage way. But two sl.Signal can't be directly connected and sl.Signal always emitted with one parameter (which is python's object - used to pass reference to sender). This similarity made in order to make console code compatible to Qt's one and avoid possible programmer confusion.

Current implementation of sl.Signal quite low level and performance-optimized. It don't keep a python's references to connected functions, and check if the function is callable only when you connecting it. Destruction of connected function will crash you program.

pycda few times faster than qcda depending on test conditions and hardware

Work logic, difference with C cda

C cda provide a set of functions to operate with contecxts and channels. First you have to create context which stores default name prefix and has some servers-timing callbacks. Than you can populate context with channels - i/o units which give you interface to data (read/write, callback for events like data update).

This logic propagated to Python, but there are few differences:

  • Context and channels are level higher then corresponding entities from C. They automatize data acquisition which is manual at C cda level.
  • Context is'n required in python. If you create channel without context default context is used, with default name prefix 'cx::' - using CXv4 protocol by default.
  • C cda don't makes a difference in channel kind, but pycda has three kinds of channels: scalar channel, scalar double channel and vector channel. Channels have almost the same interface, but internal work significantly different for each case.
  • cda formulas are not implemented yet.

module reference

####pycda specific reference main_loop() - run CX scheduler main loop. Use it to start main loop of pycda app.
break_() - break CX scheduler main loop. CX sheduler do not know about python's sys.exit(), and will ignore it. Use this function instead
Signal - multi-callback-handler class. it's interface looks like pyqtSignals, but there are some significant differences. For simple use there are connect(python-callable) and disconnect(python-callable) methods.

pycda and qcda common classes and functions

Context - cda context class
DChan(name, context=None) - scalar double channel class
Chan(name, context=None, dtype=CXDTYPE_DOUBLE) - general scalar channel class
VChan(name, context=None, dtype=CXDTYPE_DOUBLE, max_nelems=1) - vector channel class

Common for all channels:
val - a channel last-known value (will return corresponding python's type or numpy array for vchan)
time - server provided update time, int64 in microseconds from unix epoch
setValue(value) - function to set a value (number for scalars or numpy array for vchan)
valueMeasured, valueChanged - signals to connect your callbacks

CX dtype - is a specially coded int, is's better to use following constants:

CXDTYPE_INT8
CXDTYPE_INT16
CXDTYPE_INT32
CXDTYPE_INT64
CXDTYPE_UINT8
CXDTYPE_UINT16
CXDTYPE_UINT32
CXDTYPE_UINT64
CXDTYPE_SINGLE
CXDTYPE_DOUBLE
CXDTYPE_TEXT
CXDTYPE_UCTEXT

There are also some cython-only classes (python-unusable):
'CdaObject' - generalized cda object with callback data keeping. Base class for Context and BaseChan.
'BaseChan' - base class for all channel classes.

Examples:
simple pycda example:

#!/usr/bin/env python
import pycx.pycda as cda # Let's import pycda

# this makes python interpreter exit on Ctrl-C
import signal
signal.signal(signal.SIGINT, signal.SIG_DFL)

# defining a collback or slot does'n matter how to name it
# function accepts on argument - a channel which sends this signal
# really it's a pointer to channel
def printval(chan):
    print chan.val

chan = cda.Chan("localhost:1.name.0") # register a channel with given name
chan.valueMeasured.connect(printval)   # connect our callback to channel's signal

cda.main_loop() # run main loop

The same example for Qt:

#!/usr/bin/env python
import sys
from PyQt4 import QtCore # import PyQt
import pycx.qcda as cda # import qcda

# this makes python interpreter exit on Ctrl-C
import signal
signal.signal(signal.SIGINT, signal.SIG_DFL)

# defining a collback or slot does'n matter how to name it
# function accepts on argument - a channel which sends this signal
# really it's a pointer to channel
def printval(chan):
    print chan.val

app = QtCore.QCoreApplication(sys.argv) # first you need Qt app

chan = cda.Chan("localhost:1.name.0") # register a channel with given name
chan.valueMeasured.connect(printval)   # connect our collback to channel's signal

sys.exit(app.exec_()) # run main loop