From 1b7d30e3061f1397f8f75ceee2a96e03002d12e4 Mon Sep 17 00:00:00 2001 From: EricPei20 Date: Fri, 29 May 2026 14:17:15 -0700 Subject: [PATCH] Updated get_property_specifications --- deapi/client.py | 101 ++++++++++++++++++++----------------- deapi/data_types.py | 83 ++++++++++++++++++++++++++++++ deapi/tests/test_client.py | 20 ++++---- 3 files changed, 149 insertions(+), 55 deletions(-) diff --git a/deapi/client.py b/deapi/client.py index e364792..fc41e6b 100644 --- a/deapi/client.py +++ b/deapi/client.py @@ -31,6 +31,9 @@ Attributes, Histogram, PropertySpec, + PropertySpecifications, + PropertyType, + PropertyAllowableType, MovieBufferStatus, MovieBufferInfo, DataType, @@ -423,16 +426,18 @@ def get_property_spec(self, property_name: str): ) def get_property_specifications(self, property_name): """ - Get a list of allowed values for a property of the camera on DE-Server - Only works for DE-MC version greater or equal to 2.7.4 + Get the specifications for a property of the camera on DE-Server + Only supported for DE-MC version 2.7.5 and above Parameters ---------- property_name : str - The name of the property to get the allowed values for + The name of the property to get the specifications for """ - t0 = self.GetTime() - values = False + if self.commandVersion < 13: + log.error("get_property_specifications is only supported for server version 2.7.5 and above.") + return None + command = self._addSingleCommand( self.GET_PROPERTY_SPECIFICATIONS, property_name ) @@ -442,48 +447,53 @@ def get_property_specifications(self, property_name): values = self.__getParameters(response.acknowledge[0]) - propSpec = PropertySpec() - propSpec.dataType = values[0] - propSpec.valueType = values[1] - propSpec.category = values[-4] - propSpec.options = list(values[2:-4]) - propSpec.defaultValue = str(values[-3]) - propSpec.currentValue = str(values[-2]) - propSpec.readOnly = bool(values[-1]) - - optionsLength = len(propSpec.options) - - if propSpec.valueType == "Range": - if optionsLength == 2: - rangeString = "" - for i in range(optionsLength): - if propSpec.dataType == "Integer": - rangeString += str(int(propSpec.options[i])) - else: - rangeString += str(propSpec.options[i]) - if i == 0: - rangeString += str(" - ") - - propSpec.options.append(rangeString) - - if propSpec.valueType == "Set": - for i in range(optionsLength): - if propSpec.defaultValue == propSpec.options[i]: - if propSpec.defaultValue != "": - propSpec.options[i] = propSpec.defaultValue + str("*") - else: - emptyStringIndex = i - if propSpec.defaultValue == "": - propSpec.options.pop(emptyStringIndex) - - if "allow_all" in propSpec.valueType: - propSpec.options = "" - elif propSpec.dataType == "String": - propSpec.options = str(list(map(lambda a: str(a), propSpec.options)))[1:-1] + if not values or len(values) == 0: + log.error(f"get_property_specifications({property_name}) failed, parameter size not matched.") + return None + + prop_spec = PropertySpecifications() + + param_id = 0 + data_type = values[param_id] + param_id += 1 + if data_type == "String": + prop_spec.prop_type = PropertyType.String + elif data_type == "Float": + prop_spec.prop_type = PropertyType.Float + elif data_type == "Integer": + prop_spec.prop_type = PropertyType.Integer else: - propSpec.options = str(propSpec.options)[1:-1] + log.error(f"get_property_specifications({property_name}) failed, property type not matched.") + return None + + prop_allowable_type = values[param_id] + param_id += 1 + + if prop_allowable_type == "Range": + prop_spec.prop_allowable_type = PropertyAllowableType.Range + if prop_spec.prop_type in (PropertyType.Float, PropertyType.Integer): + prop_spec.min_value = values[param_id] + param_id += 1 + prop_spec.max_value = values[param_id] + param_id += 1 + else: + log.error(f"get_property_specifications({property_name}) failed, cannot read the min/max value.") + return None + elif prop_allowable_type == "Set": + prop_spec.prop_allowable_type = PropertyAllowableType.Set + prop_spec.values = list(values[param_id:-4]) + elif prop_allowable_type == "AllowAll": + prop_spec.prop_allowable_type = PropertyAllowableType.AllowAll + else: + log.error(f"get_property_specifications({property_name}) failed, unknown allowable type.") + return None - return propSpec + prop_spec.category = values[-4] + prop_spec.default_value = values[-3] + prop_spec.current_value = values[-2] + prop_spec.read_only = values[-1] + + return prop_spec @deprecated_argument( name="propertyName", since="5.2.0", alternative="property_name" @@ -3133,6 +3143,7 @@ def ParseChangedProperties(self, changedProperties, response): SetCurrentCamera = set_current_camera ListProperties = list_properties GetPropertySpec = get_property_spec + GetPropertySpecifications = get_property_specifications # PropertyValidValues = property_valid_values GetProperty = get_property SetProperty = set_property diff --git a/deapi/data_types.py b/deapi/data_types.py index c512471..bd363d2 100644 --- a/deapi/data_types.py +++ b/deapi/data_types.py @@ -168,6 +168,19 @@ class BinningMethod(IntEnum): FOURIERCROP = 3 +class PropertyType(IntEnum): + Undef = 0 + String = 1 + Float = 2 + Integer = 3 + + +class PropertyAllowableType(IntEnum): + Range = 0 + Set = 1 + AllowAll = 2 + + class Attributes: """Class to hold attributes for getting the result of an image acquisition @@ -544,6 +557,7 @@ def __repr__(self): class PropertySpec: """Class to hold the specification of a property in the DE API + Deprecated since DE-MC 2.7.4 Parameters ---------- @@ -600,6 +614,75 @@ def __repr__(self): ) +class PropertySpecifications: + """Class to hold the specification of a property in the DE API + + Parameters + ---------- + prop_type : str, optional + Type of the property + prop_allowable_type : str, optional + Allowable type of the property + min_value : float, optional + Minimum value of the property + max_value : float, optional + Maximum value of the property + values : list, optional + List of values for the property + default_value : str, optional + Default value of the property + current_value : str, optional + Current value of the property + read_only : bool, optional + Whether the property is read-only + """ + + def __init__( + self, + prop_type: PropertyType = None, + prop_allowable_type: PropertyAllowableType = None, + min_value: float = None, + max_value: float = None, + values: list = None, + category: str = None, + default_value: str = None, + current_value: str = None, + read_only: bool = None, + ): + self.prop_type = prop_type + self.prop_allowable_type = prop_allowable_type + self.min_value = min_value + self.max_value = max_value + self.values = values + self.category = category + self.default_value = default_value + self.current_value = current_value + self.read_only = read_only + + prop_type = None # Undef | String | Float | Integer + prop_allowable_type = None # Range | Set | AllowAll + min_value = None # Minimum value for Range type + max_value = None # Maximum value for Range type + values = None # List of values for Set allowable type + category = None # "Alias" | "Advanced" | "Basic" | "Deprecated" | "Engineering" | Obsolete" + default_value = None # Default value + current_value = None # Current value + read_only = False # Read-only property + + def __repr__(self): + return ( + f"PropertySpecifications(prop_type={self.prop_type}, " + f"prop_allowable_type={self.prop_allowable_type}, " + f"min_value={self.min_value}, " + f"max_value={self.max_value}, " + f"values={self.values}, " + f"category={self.category}, " + f"default_value={self.default_value}, " + f"current_value={self.current_value}, " + f"read_only={self.read_only})" + ) + + class PropertyCollection: """Class to interact with collections of properties in the DE API diff --git a/deapi/tests/test_client.py b/deapi/tests/test_client.py index 8d6d0d9..5c585da 100644 --- a/deapi/tests/test_client.py +++ b/deapi/tests/test_client.py @@ -3,7 +3,7 @@ from deapi import Client, Histogram import pytest from deapi.data_types import ( - PropertySpec, + PropertySpecifications, VirtualMask, MovieBufferStatus, ContrastStretchType, @@ -172,23 +172,23 @@ def test_bin_property_set(self, client): @pytest.mark.server @pytest.mark.parametrize("bin_sw", [1, 2, 4]) - def test_property_spec_set(self, client, bin_sw): + def test_property_specifications_set(self, client, bin_sw): client.set_property("Hardware Binning X", 1) client.set_property("Hardware Binning Y", 1) client.set_property("Binning Y", bin_sw) - sp = client.get_property_spec("Binning Y") - assert isinstance(sp, PropertySpec) - assert sp.currentValue == str(bin_sw) + sp = client.get_property_specifications("Binning Y") + assert isinstance(sp, PropertySpecifications) + assert sp.current_value == str(bin_sw) assert ( - sp.options - == "'1*', '2', '4', '8', '16', '32', '64', '128', '256', '512', '1024'" + sp.values + == ['1', '2', '4', '8', '16', '32', '64', '128', '256', '512', '1024'] ) client.set_property("Hardware Binning X", 2) client.set_property("Hardware Binning Y", 2) - sp = client.get_property_spec("Binning Y") - assert sp.currentValue == str(bin_sw) + sp = client.get_property_specifications("Binning Y") + assert sp.current_value == str(bin_sw) assert ( - sp.options == "'1*', '2', '4', '8', '16', '32', '64', '128', '256', '512'" + sp.values == ['1', '2', '4', '8', '16', '32', '64', '128', '256', '512'] ) @pytest.mark.parametrize("bin", [1, 2])