if allow_None is True, property supports None apart from its own type
readonly (being True) makes the property read-only or execute only 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.
fromhololinked.coreimportThingfromhololinked.core.propertiesimportString,Number,TypedListclassOceanOpticsSpectrometer(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: strintegration_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",)
# allow_Nonespectrometer.custom_background_intensity=None# OKspectrometer.custom_background_intensity=[]# OKspectrometer.custom_background_intensity=None# OK
# constant = True, mandatorily needs allow_None = Truespectrometer.model=None# OK - constant accepts None when initially Nonespectrometer.model="USB2000+"# OK - can be set oncespectrometer.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 with a readable name (for example).
classOceanOpticsSpectrometer(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
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. If a setter/deleter is given, getter is mandatory. In this case, default is also ignored & the getter is always executed.
classIDSCamera(Thing):"""Camera example object"""frame_rate=Number(default=1,bounds=(0,40),doc="frame rate of the camera",crop_to_bounds=True,)@frame_rate.setterdefset_frame_rate(self,value):setFPS=ueye.double()ret=ueye.is_SetFrameRate(self.device,value,setFPS)ifret!=ueye.IS_SUCCESS:raiseException("could not set frame rate")@frame_rate.getterdefget_frame_rate(self)->float:getFPS=ueye.double()doc="frame rate of the camera",fget=get_frame_rate,fset=set_frame_rate,)id=Integer(default=1,# not ignoredallow_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 1print(cam.frame_rate)# does not print default,# but the actual value in device after invoking the getter
classIDSCamera(Thing):"""Camera example object"""defset_frame_rate(self,value):setFPS=ueye.double()ret=ueye.is_SetFrameRate(self.device,value,setFPS)ifret!=ueye.IS_SUCCESS:raiseException("could not set frame rate")defget_frame_rate(self)->float:getFPS=ueye.double()ret=ueye.is_SetFrameRate(self.device,ueye.IS_GET_FRAMERATE,getFPS)ifret!=ueye.IS_SUCCESS:raiseException("could not get frame rate")returngetFPS.valueframe_rate=Number(default=1,# ignoredbounds=(0,40),crop_to_bounds=True,doc="frame rate of the camera",fget=get_frame_rate,fset=set_frame_rate,)
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.
classErrorCodes(IntEnum):IS_NO_SUCCESS=-1IS_SUCCESS=0IS_INVALID_CAMERA_HANDLE=1IS_CANT_OPEN_DEVICE=3IS_CANT_CLOSE_DEVICE=4@classmethoddefjson(cls):# code to code name - opposite of enum definitionreturn{value.value:nameforname,valueinvars(cls).items()ifisinstance(value,cls)}classIDSCamera(Thing):"""Camera example object"""deferror_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,# ignored)if__name__=="__main__":cam=IDSCamera(id="camera")print("errorcodes class level",IDSCamera.error_codes)# prints error codesprint("errorcodes instance level",cam.error_codes)# prints error codesprint(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 property 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:
importloggingfromhololinked.core.propertiesimportClassSelectorclassThing(metaclass=ThingMeta):"""Subclass from here to expose hardware or python objects on the network"""logger=ClassSelector(class_=logging.Logger,default=None,allow_None=True,remote=False,# does not make sense to expose the logger objectdoc="""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):
classIDSCamera(Thing):"""Camera example object"""defget_pixelclock(self)->int:cint_in=ueye.uint()ret=ueye.is_PixelClock(self.handle,ueye.IS_PIXELCLOCK_CMD_GET,cint_in,ueye.sizeof(cint_in),)assertreturn_code_OK(self.handle,ret)returncint_in.valuedefset_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),)assertreturn_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 write operations are called by clients. Local write operations are always executed irrespective of the state machine state. A read operation is always executed 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.
classIDSCamera(Thing):"""Camera example object"""defset_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),)assertreturn_code_OK(self.handle,ret)defget_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),)assertreturn_code_OK(self.handle,ret)returncdbl_out.valueexposure_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: floatif__name__=="__main__":cam=IDSCamera(id="camera")foriinrange(10):cam.exposure_time=i*10.0# pushes change event to a clientcam.capture_image()# captures an image with current exposure time
metadata
metadata is a free-flow 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, server crash or any other reason.
db_init only loads a property from database. When the property 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/write operation is called.
db_persist stores and loads the property from the database. property value is assigned after __init__() method, therefore its recommended to ensure that the device connection is established at __init__() itself. Otherwise, errors should be expected.
Supported databases are MySQL, Postgres, SQLite & Mongo currently. Look at database how-to for supply database configuration.