diff --git a/sentio_prober_control/Sentio/CommandGroups/ProbeCommandGroup.py b/sentio_prober_control/Sentio/CommandGroups/ProbeCommandGroup.py index fa75b64..3a0b1e2 100644 --- a/sentio_prober_control/Sentio/CommandGroups/ProbeCommandGroup.py +++ b/sentio_prober_control/Sentio/CommandGroups/ProbeCommandGroup.py @@ -3,7 +3,7 @@ from sentio_prober_control.Sentio.Enumerations import ProbePosition, XyReference, ZReference, ChuckSite from sentio_prober_control.Sentio.Response import Response from sentio_prober_control.Sentio.CommandGroups.CommandGroupBase import CommandGroupBase - +from sentio_prober_control.Sentio.Compatibility import Compatibility, CompatibilityLevel class ProbeCommandGroup(CommandGroupBase): """This command group contains functions for working with motorized prober. @@ -143,7 +143,12 @@ def get_probe_z(self, probe: ProbePosition, ref: ZReference) -> float: Returns: The z position in micrometer. """ - self.comm.send(f"get_positioner_z {probe.to_string()},{ref.to_string()}") + + # Note: + # Even in Sentio 24.x the ref parameter is expected in long form! This is why we use + # CompatibilityLevel.Sentio_25_2 here. + self.comm.send(f"get_positioner_z {probe.to_string()},{ref.to_string(CompatibilityLevel.Sentio_25_2)}") + resp = Response.check_resp(self.comm.read_line()) return float(resp.message()) @@ -225,7 +230,10 @@ def move_probe_z(self, probe: ProbePosition, ref: ZReference, z: float) -> float The z position after the move in micrometer (from zero). """ - self.comm.send(f"move_positioner_z {probe.to_string()},{ref.to_string()},{z}") + # Note: + # Even in Sentio 24.x the ref parameter is expected in long form! This is why we use + # CompatibilityLevel.Sentio_25_2 here. + self.comm.send(f"move_positioner_z {probe.to_string()},{ref.to_string(CompatibilityLevel.Sentio_25_2)},{z}") resp = Response.check_resp(self.comm.read_line()) return float(resp.message()) diff --git a/sentio_prober_control/Sentio/CommandGroups/SiPHCommandGroup.py b/sentio_prober_control/Sentio/CommandGroups/SiPHCommandGroup.py index db37806..cae3ea3 100644 --- a/sentio_prober_control/Sentio/CommandGroups/SiPHCommandGroup.py +++ b/sentio_prober_control/Sentio/CommandGroups/SiPHCommandGroup.py @@ -1,6 +1,6 @@ from typing import Tuple - +from sentio_prober_control.Sentio.Compatibility import Compatibility, CompatibilityLevel from sentio_prober_control.Sentio.Enumerations import ProbePosition, UvwAxis, FiberType, CompatibilityLevel from sentio_prober_control.Sentio.Response import Response from sentio_prober_control.Sentio.CommandGroups.CommandGroupBase import CommandGroupBase @@ -228,8 +228,7 @@ def download_graph_data(self, file_path: str, file_name: str) -> None: Returns: A Response object containing the command execution status. """ - if self.prober.compatibility_level < CompatibilityLevel.Sentio_25: - raise NotImplementedError(f"download_graph_data is not supported in compatibility level {self.prober.compatibility_level}.") + Compatibility.assert_min(CompatibilityLevel.Sentio_25_2) self.comm.send(f"siph:download_graph_data {file_path}, {file_name}") Response.check_resp(self.comm.read_line()) diff --git a/sentio_prober_control/Sentio/Compatibility.py b/sentio_prober_control/Sentio/Compatibility.py new file mode 100644 index 0000000..b5b8a2a --- /dev/null +++ b/sentio_prober_control/Sentio/Compatibility.py @@ -0,0 +1,38 @@ +from enum import IntEnum + + +class CompatibilityLevel(IntEnum): + """ Compatibility level of the prober. + + The compatibility level is determined at time of instantiating the prober class. + It is used to determine which features are available in the prober. This enum + only contains SENTIO versions which introduces API changes. + """ + Undefined = 0, + Sentio_24 = 1, + Sentio_25_2 = 2 + + +class Compatibility: + """Compatibility class to determine the compatibility level of the prober. + + The compatibility level is determined at time of instantiating the prober class. + It is used to determine which features are available in the prober. This class + only contains SENTIO versions which introduces API changes. + """ + + # Default compatibility level is Undefined + level : CompatibilityLevel = CompatibilityLevel.Undefined + + @staticmethod + def assert_min(level : CompatibilityLevel) -> None: + """Asserts that the compatibility level is at least the given level. + + Args: + level (CompatibilityLevel): The minimum compatibility level required. + + Raises: + RuntimeError: If the current compatibility level is lower than the required level. + """ + if Compatibility.level < level: + raise RuntimeError(f"Compatibility level {Compatibility.level} is lower than required {level}.") \ No newline at end of file diff --git a/sentio_prober_control/Sentio/Enumerations.py b/sentio_prober_control/Sentio/Enumerations.py index 8401572..1f07cea 100644 --- a/sentio_prober_control/Sentio/Enumerations.py +++ b/sentio_prober_control/Sentio/Enumerations.py @@ -2,6 +2,9 @@ from deprecated import deprecated +from sentio_prober_control.Sentio.Compatibility import CompatibilityLevel, Compatibility + + class AccessLevel(Enum): """Specifies a SENTIO access level. @@ -376,17 +379,6 @@ def to_string(self): return switcher.get(self, "Invalid ColorScheme") -class CompatibilityLevel(Enum): - """ Compatibility level of the prober. - - The compatibility level is determined at time of instantiating the prober class. - It is used to determine which features are available in the prober. - """ - Undefined = 0, - Sentio_24 = 1, - Sentio_25 = 2 - - @deprecated(reason="duplicated; use DieCompensationMode instead.") class Compensation(Enum): Lateral = 0 @@ -2056,18 +2048,43 @@ class ZReference(Enum): Ready = 7 RealPos = 8 - def to_string(self): - switcher = { - ZReference.Contact: "C", - ZReference.Separation: "S", - ZReference.Hover: "H", - ZReference.Zero: "Z", - ZReference.Current: "R", - ZReference.Vce1: "VCE01", - ZReference.Vce2: "VCE02", - ZReference.Ready: "Ready", - ZReference.RealPos: "RealPos", - } + def to_string(self, compat_level : CompatibilityLevel = CompatibilityLevel.Undefined) -> str: + if compat_level == CompatibilityLevel.Undefined: + compat_level = Compatibility.level + + if compat_level < CompatibilityLevel.Sentio_25_2: + # This is the original implementation for SENTIO <25.2. + # Older versions of SENTIO are inconsistent with what they expect + # as remote command parameters. Most older remote commands accept both + # long and short form of the z reference although some may only work with + # the long form. + switcher = { + ZReference.Contact: "C", + ZReference.Separation: "S", + ZReference.Hover: "H", + ZReference.Zero: "Z", + ZReference.Current: "R", + ZReference.Vce1: "VCE01", + ZReference.Vce2: "VCE02", + ZReference.Ready: "Ready", + ZReference.RealPos: "RealPos", + } + else: + # This is for SENTIO >=25.2. + # Newer versions of SENTIO always accept both long and short versions. + # For clarity the long version is used exclusively. + switcher = { + ZReference.Contact: "Contact", + ZReference.Separation: "Separation", + ZReference.Hover: "Hover", + ZReference.Zero: "Zero", + ZReference.Current: "Current", + ZReference.Vce1: "VCE01", + ZReference.Vce2: "VCE02", + ZReference.Ready: "Ready", + ZReference.RealPos: "RealPos", + } + return switcher.get(self, "Invalid chuck z reference") @staticmethod diff --git a/sentio_prober_control/Sentio/ProberSentio.py b/sentio_prober_control/Sentio/ProberSentio.py index b1c120c..7895a8e 100644 --- a/sentio_prober_control/Sentio/ProberSentio.py +++ b/sentio_prober_control/Sentio/ProberSentio.py @@ -10,7 +10,6 @@ ChuckPositionHint, ChuckSite, ChuckSpeed, - CompatibilityLevel, ThetaReference, DialogButtons, LoadPosition, @@ -28,7 +27,7 @@ XyReference, ZReference ) - +from sentio_prober_control.Sentio.Compatibility import CompatibilityLevel, Compatibility from sentio_prober_control.Sentio.ProberBase import ProberBase, ProberException from sentio_prober_control.Sentio.Response import Response from sentio_prober_control.Communication.CommunicatorBase import CommunicatorBase @@ -62,6 +61,7 @@ class SentioCommunicationType(Enum): Gpib = 1 Visa = 2 + class SentioProber(ProberBase): """This class represents the SENTIO probe station in python. @@ -90,9 +90,7 @@ def __init__(self, comm: CommunicatorBase): """ ProberBase.__init__(self, comm) - self.__name = "SentioProber" - self.comm.send("*RCS 1") # switch to the native SENTIO remote command set - + # Standard command groups self.aux: AuxCommandGroup = AuxCommandGroup(self) self.loader: LoaderCommandGroup = LoaderCommandGroup(self) self.map: WafermapCommandGroup = WafermapCommandGroup(self) @@ -104,16 +102,10 @@ def __init__(self, comm: CommunicatorBase): self.vision: VisionCommandGroup = VisionCommandGroup(self) self.setup: SetupCommandGroup = SetupCommandGroup(self) - # Command groups for stages; Only available for Sentio > 25.2 - self.scope: StageCommandGroup = StageCommandGroup(self, Stage.Scope, True) - self.chuck: StageCommandGroup = StageCommandGroup(self, Stage.Chuck, False) - - # deprecated command groups; may be removed at any time. - # DO NOT USE THEM IN NEW CODE! - self.compensation: CompensationCommandGroup = CompensationCommandGroup(self) + self.__name = "SentioProber" + self.comm.send("*RCS 1") # switch to the native SENTIO remote command set version : str = self.status.get_version() - self._compatibility_level = CompatibilityLevel.Undefined # Extract version string match = re.search(r"Version:\s*([\d\.]+)", version) @@ -122,32 +114,30 @@ def __init__(self, comm: CommunicatorBase): parts = version.split(".") major = int(parts[0]) if len(parts) > 0 else None minor = int(parts[1]) if len(parts) > 1 else None + release = int(parts[2]) if len(parts) > 2 else None if major==24: - self._compatibility_level = CompatibilityLevel.Sentio_24 - elif major==25: - self._compatibility_level = CompatibilityLevel.Sentio_25 + Compatibility.level = CompatibilityLevel.Sentio_24 + elif major==25 and (minor==2 or (minor==1 and release==99)): + Compatibility.level = CompatibilityLevel.Sentio_25_2 else: - self._compatibility_level = CompatibilityLevel.Undefined + Compatibility.level = CompatibilityLevel.Undefined - @property - def compatibility_level(self) -> CompatibilityLevel: - """Get the compatibility level of the prober. + # + # More command groups not supported by all SENTIO versions. + # - The compatibility level is determined at time of instantiating the prober - class by executing the prober.status.get_verison() command. It is used to - determine which features are available in the probers remote command API. - """ - return self._compatibility_level + # Command groups for stages; Only available for Sentio > 25.2 + if Compatibility.level >= CompatibilityLevel.Sentio_25_2: + self.scope: StageCommandGroup = StageCommandGroup(self, Stage.Scope, True) + self.chuck: StageCommandGroup = StageCommandGroup(self, Stage.Chuck, False) - @compatibility_level.setter - def compatibility_level(self, value: CompatibilityLevel) -> None: - """Set the compatibility level of the prober. + # + # Deprecated command groups + # + # DO NOT USE THEM IN NEW CODE! They will be removed in the future! + + self.compensation: CompensationCommandGroup = CompensationCommandGroup(self) - This setter allows overriding the compatibility level of the prober to set it - to a specific version. This can be done to keep compatibility with older - scripts. - """ - self._compatibility_level = value def abort_command(self, cmd_id: int) -> Response: """Stop an ongoing asynchronous remote command.