Skip to content

Common arguments to all properties

API Reference

allow_None, constant & readonly

  • if allow_None is True, property supports None apart from its own type
  • readonly (being True) makes the property read-only or execute the getter method
  • constant (being True), again makes the property read-only but can be set once if allow_None is True. This is useful to set the property once at __init__() but remain constant after that.
allow None, constant and readonly
from hololinked.server import Thing
from hololinked.server.properties import String, Number, TypedList

class OceanOpticsSpectrometer(Thing):
    """Spectrometer example object """

    serial_number = String(default="USB2+H15897", allow_None=False, readonly=True, 
                        doc="serial number of the spectrometer (string)",
                        label="serial number") # type: str

    integration_time = Number(default=1000, bounds=(0.001, None), allow_None=False,
                            crop_to_bounds=True, label="Integration Time (ms)",
                            doc="integration time of measurement in milliseconds")

    model = String(default=None, allow_None=True, constant=True, 
                label="device model", doc="model of the connected spectrometer")

    custom_background_intensity = TypedList(item_type=(float, int), default=None, 
                    allow_None=True, label="Custom Background Intensity",
                    doc="user provided background substraction intensity")


spectrometer = OceanOpticsSpectrometer(id='spectrometer1', serial_number='S14155')
allow None, constant and readonly
1
2
3
spectrometer.custom_background_intensity = None # OK 
spectrometer.custom_background_intensity = [] # OK
spectrometer.custom_background_intensity = None # OK
allow None, constant and readonly
spectrometer.integration_time = None # NOT OK, raises TypeError
spectrometer.integration_time = 2000 # OK
allow None, constant and readonly
spectrometer.serial_number = "USB2+H15897" # NOT OK - raises ValueError
allow None, constant and readonly
1
2
3
spectrometer.model = None # OK - constant accepts None when initially None
spectrometer.model = 'USB2000+' # OK - can be set once 
spectrometer.model = None # NOT OK - raises ValueError

doc and label

doc allows clients to fetch a docstring for the property. label can be used to show the property in a GUI for example.

allow None, constant and readonly
1
2
3
4
5
6
7
8
9
from hololinked.server import Thing
from hololinked.server.properties import String, Number, TypedList

class OceanOpticsSpectrometer(Thing):
    """Spectrometer example object """

    integration_time = Number(default=1000, bounds=(0.001, None), allow_None=False,
                            crop_to_bounds=True, label="Integration Time (ms)",
                            doc="integration time of measurement in milliseconds")

default, fget, fset & fdel

To provide a getter-setter (& deleter) method is optional. If none given, when the property is set/written, the value is stored inside the instance's __dict__ under the name <given property name >_param_value (for example, serial_number_param_value for serial_number). In layman's terms, __dict__ is the internal map where the attributes of the object are stored by python.

When a value assignment was never called on the property, default is returned when reading the value. This is the purpose of the default argument. If a setter/deleter is given, getter is mandatory. In this case, default is also ignored & the getter is always executed.

class IDSCamera(Thing):
    """Camera example object"""

    frame_rate = Number(default=1, bounds=(0, 40),
                        doc="frame rate of the camera", crop_to_bounds=True)

    @frame_rate.setter 
    def set_frame_rate(self, value):
        setFPS = ueye.double()
        ret = ueye.is_SetFrameRate(self.device, value, setFPS)
        if ret != ueye.IS_SUCCESS:
            raise Exception("could not set frame rate")

    @frame_rate.getter 
    def get_frame_rate(self) -> float:
        getFPS = ueye.double()
        ret = ueye.is_SetFrameRate(self.device, ueye.IS_GET_FRAMERATE, getFPS)
        if ret != ueye.IS_SUCCESS:
            raise Exception("could not get frame rate")
        return getFPS.value

    id = Integer(default=1, allow_None=True, bounds=(1, 255), 
                doc="Camera ID shown in IDS Camera Manager (not dev. ID)")

if __name__ == '__main__':
    cam = IDSCamera(id='camera')
    print(cam.id) # prints 1
    print(cam.frame_rate) # does not print default, 
    # but the actual value in device after invoking the getter
class IDSCamera(Thing):
    """Camera example object"""

    def set_frame_rate(self, value):
        setFPS = ueye.double()
        ret = ueye.is_SetFrameRate(self.device, value, setFPS)
        if ret != ueye.IS_SUCCESS:
            raise Exception("could not set frame rate")

    def get_frame_rate(self) -> float:
        getFPS = ueye.double()
        ret = ueye.is_SetFrameRate(self.device, ueye.IS_GET_FRAMERATE, getFPS)
        if ret != ueye.IS_SUCCESS:
            raise Exception("could not get frame rate")
        return getFPS.value

    frame_rate = Number(default=1, bounds=(0, 40), crop_to_bounds=True,
      doc="frame rate of the camera", fget=get_frame_rate, fset=set_frame_rate)

    id = Integer(default=1, allow_None=True, bounds=(1, 255), 
                doc="Camera ID shown in IDS Camera Manager (not dev. ID)")

if __name__ == '__main__':
    cam = IDSCamera(id='camera')
    print(cam.id) # prints 1
    print(cam.frame_rate) # does not print default, 
    # but the actual value in device after invoking the getter

If default is desirable, one has to return it manually in the getter method by accessing the property descriptor object directly.

class_member

If class_member is True, the value is set in the class' __dict__ (i.e. becomes a class attribute) instead of instance's __dict__ (instance's attribute). Custom getter-setter-deleter are not compatible with this option currently. class_member takes precedence over fget-fset-fdel, which in turn has precedence over default.

class member
class ErrorCodes(IntEnum):
    IS_NO_SUCCESS = -1
    IS_SUCCESS = 0
    IS_INVALID_CAMERA_HANDLE = 1
    IS_CANT_OPEN_DEVICE = 3
    IS_CANT_CLOSE_DEVICE = 4

    @classmethod
    def json(cls):
        # code to code name - opposite of enum definition
        return {
            value.value : name for name, value in vars(cls).items() if isinstance(
                                                                    value, cls)}

class IDSCamera(Thing):
    """Camera example object"""

    def error_codes_misplaced_getter(self):
        return {"info" : "this getter is never called"}

    error_codes = Property(readonly=True, default=ErrorCodes.json(), 
                        class_member=True, doc="error codes raised by IDS library",
                        fget=error_codes_misplaced_getter)    

if __name__ == '__main__':
    cam = IDSCamera(id='camera')
    print(cam.id) # prints 1
    print("error codes class level", IDSCamera.error_codes) # prints error codes
    print("error codes instance level", cam.error_codes) # prints error codes
    print(IDSCamera.error_codes == cam.error_codes) # prints True

class_member can still be used with a default value if there is no custom fget-fset-fdel.

remote

setting remote to False makes the inaccessible to a client but accessible to the object locally. This is still useful to type-restrict python attributes to provide an interface to other developers using your class, for example, when someone else inherits your Thing. For example, the Thing's logger is implemented in this fashion:

local properties
import logging 
from hololinked.server.properties import ClassSelector

class Thing:
    """Subclass from here to expose hardware or python objects on the network"""

    logger = ClassSelector(class_=logging.Logger, default=None, allow_None=True,
                        remote=False,
                        doc="""logging.Logger instance to print log messages. 
                            Default logger with a IO-stream handler and network 
                            accessible handler is created if none supplied."""
                        ) # type: logging.Logger

state

When state is specifed, the property is writeable only when the Thing's StateMachine is in that specified state (or in the list of allowed states):

state machine state
class IDSCamera(Thing):
    """Camera example object"""

    def get_pixelclock(self) -> int:
        cint_in = ueye.uint()
        ret = ueye.is_PixelClock(self.handle, ueye.IS_PIXELCLOCK_CMD_GET,
                                    cint_in, ueye.sizeof(cint_in))
        assert return_code_OK(self.handle, ret)
        return cint_in.value

    def set_pixelclock(self, value : int) -> None:
        cint_in = ueye.uint(value)
        ret = ueye.is_PixelClock(self.handle, ueye.IS_PIXELCLOCK_CMD_SET,
                                    cint_in, ueye.sizeof(cint_in))
        assert return_code_OK(self.handle, ret)

    pixel_clock = Integer(doc="Pixel clock in MHz", bounds=(0, None), state=["ON"], 
                    metadata=dict(unit='MHz'), inclusive_bounds=(False, True),
                    fget=get_pixelclock, fset=set_pixelclock) # type: int

This is also currently applicable only when set operations are called by clients. Local set operations are always executed irrespective of the state machine state. A get operation is always executed as well even from the clients irrespective of the state.

observable

Observable properties push change events when the property is set or read. This is useful when one wants to monitor the property for changes without polling from the client. The payload of the change event is the new value of the property.

state machine state
class IDSCamera(Thing):
    """Camera example object"""

    def set_exposure(self, value : float) -> None:
        cdbl_in = ueye.double(value)
        ret = ueye.is_Exposure(self.handle, ueye.IS_EXPOSURE_CMD_SET_EXPOSURE, 
                            cdbl_in, ueye.sizeof(cdbl_in))
        assert return_code_OK(self.handle, ret)

    def get_exposure(self) -> float:
        cdbl_out = ueye.double()
        ret = ueye.is_Exposure(self.handle, ueye.IS_EXPOSURE_CMD_GET_EXPOSURE, 
                            cdbl_out, ueye.sizeof(cdbl_out))
        assert return_code_OK(self.handle, ret)
        return cdbl_out.value

    exposure_time = Number(bounds=(0, None), inclusive_bounds=(False, True), 
                        doc="Exposure time for image in milliseconds",
                        observable=True, metadata=dict(unit='ms'),
                        fget=get_exposure, fset=set_exposure) # type: float

metadata

metadata is a dictionary that allows storing arbitrary metadata about the property. For example, one can store units of the physical quantity.

db_init, db_commit & db_persist

Properties can be stored in a file or a database and loaded from them when the Thing is stopped and restarted. This is useful especially to preserve the settings of the hardware when the server undergoes a restart, either through system restart or any other reason.

  • db_init only loads a property from database, when the value is changed, its not written back to the database. For this option, the value has to be pre-created in the database in some other fashion.

  • db_commit only writes the value into the database when an assignment is called.

  • db_persist both stores and loads the property from the database.

Supported databases are MySQL, Postgres & SQLite currently. Look at database how-to for supply database configuration.