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.
fromhololinked.serverimportThingfromhololinked.server.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")spectrometer=OceanOpticsSpectrometer(id='spectrometer1',serial_number='S14155')
spectrometer.model=None# OK - constant accepts None when initially Nonespectrometer.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.
fromhololinked.serverimportThingfromhololinked.server.propertiesimportString,Number,TypedListclassOceanOpticsSpectrometer(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.
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()ret=ueye.is_SetFrameRate(self.device,ueye.IS_GET_FRAMERATE,getFPS)ifret!=ueye.IS_SUCCESS:raiseException("could not get frame rate")returngetFPS.valueid=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 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,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 1print(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.
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)if__name__=='__main__':cam=IDSCamera(id='camera')print(cam.id)# prints 1print("error codes class level",IDSCamera.error_codes)# prints error codesprint("error codes 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 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.server.propertiesimportClassSelectorclassThing:"""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):
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 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.
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: 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.