diff --git a/sentio_prober_control/Sentio/CommandGroups/ProbeCommandGroup.py b/sentio_prober_control/Sentio/CommandGroups/ProbeCommandGroup.py index 3a0b1e2..3f789ca 100644 --- a/sentio_prober_control/Sentio/CommandGroups/ProbeCommandGroup.py +++ b/sentio_prober_control/Sentio/CommandGroups/ProbeCommandGroup.py @@ -1,9 +1,11 @@ from typing import Tuple -from sentio_prober_control.Sentio.Enumerations import ProbePosition, XyReference, ZReference, ChuckSite +from sentio_prober_control.Sentio.Enumerations import ProbePosition, XyReference, ZReference, ChuckSite, Stage 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 +from sentio_prober_control.Sentio.CommandGroups.StageCommandGroup import StageCommandGroup + class ProbeCommandGroup(CommandGroupBase): """This command group contains functions for working with motorized prober. @@ -20,9 +22,37 @@ class ProbeCommandGroup(CommandGroupBase): ``` """ + class _TopBottomPositionSelector: + """ This is a dummy command group for providing access to top and bottom probes. + """ + def __init__(self, prober: 'SentioProber', stage : Stage, stage_selector : str) -> None: # type: ignore + self.east : StageCommandGroup = StageCommandGroup(prober, stage, f"{stage_selector}:east") + self.west : StageCommandGroup = StageCommandGroup(prober, stage, f"{stage_selector}:west") + self.north : StageCommandGroup = StageCommandGroup(prober, stage, f"{stage_selector}:north") + self.south : StageCommandGroup = StageCommandGroup(prober, stage, f"{stage_selector}:south") + self.northeast : StageCommandGroup = StageCommandGroup(prober, stage, f"{stage_selector}:northeast") + self.northwest : StageCommandGroup = StageCommandGroup(prober, stage, f"{stage_selector}:northwest") + self.southeast : StageCommandGroup = StageCommandGroup(prober, stage, f"{stage_selector}:southeast") + self.southwest : StageCommandGroup = StageCommandGroup(prober, stage, f"{stage_selector}:southwest") + + def __init__(self, prober: 'SentioProber') -> None: super().__init__(prober) + if Compatibility.level >= CompatibilityLevel.Sentio_25_2: + self.top = self._TopBottomPositionSelector(prober, Stage.TopProbe, "probe:top") + self.bottom = self._TopBottomPositionSelector(prober, Stage.BottomProbe, "probe:bottom") + + # Top probes are also available as east, west, north, south, northeast, northwest, southeast, southwest + self.east = self.top.east + self.west = self.top.west + self.north = self.top.north + self.south = self.top.south + self.northeast = self.top.northeast + self.northwest = self.top.northwest + self.southeast = self.top.southeast + self.southwest = self.top.southwest + def async_step_probe_site(self, probe: ProbePosition, idx: int) -> int: """Start the process of stepping to a positioner site. @@ -265,9 +295,11 @@ def set_probe_home(self, probe: ProbePosition, site: ChuckSite | None = None, x: """ if site is None: self.comm.send(f"set_positioner_home {probe.to_string()}") - else: + elif x is not None and y is not None: self.comm.send(f"set_positioner_home {probe.to_string()},{site.to_string()},{x},{y}") - + else: + raise ValueError("When site is specified, x and y must also be specified.") + Response.check_resp(self.comm.read_line()) @@ -352,7 +384,7 @@ def get_probe_status(self, probe: ProbePosition) -> str: Returns: Status of positioner with 4 digits, 1st digit indicates the East Positioner, 2nd digit indicates West Positioner - 3rd digit indicates the North Positioner, 4rd digit indicates the South Positioner. + 3rd digit indicates the North Positioner, 4th digit indicates the South Positioner. """ self.comm.send(f"get_positioner_status {probe.to_string()}") diff --git a/sentio_prober_control/Sentio/CommandGroups/ScopeCommandGroup.py b/sentio_prober_control/Sentio/CommandGroups/ScopeCommandGroup.py new file mode 100644 index 0000000..1c586b9 --- /dev/null +++ b/sentio_prober_control/Sentio/CommandGroups/ScopeCommandGroup.py @@ -0,0 +1,34 @@ + +from typing import Optional + + +from sentio_prober_control.Sentio.CommandGroups.StageCommandGroup import StageCommandGroup +from sentio_prober_control.Sentio.Compatibility import CompatibilityLevel, Compatibility +from sentio_prober_control.Sentio.Enumerations import Stage + + + + + +class ScopeCommandGroup(StageCommandGroup): + """This command group contains functions for working with motorized scopes. + You are not meant to instantiate this class directly. Access it via the probe attribute + of the [SentioProber](SentioProber.md) class. + + Example: + + ```py + from sentio_prober_control.Sentio.ProberSentio import SentioProber + + prober = SentioProber.create_prober("tcpip", "127.0.0.1:35555") + prober.scope.move_probe_xy(XyReference.Current, 1000, 2000) + ``` + """ + + def __init__(self, prober: 'SentioProber', stage : Stage, stage_selector : str) -> None: # type: ignore + super().__init__(prober, stage, stage_selector) + + if Compatibility.level >= CompatibilityLevel.Sentio_25_2: + self.top : StageCommandGroup = StageCommandGroup(self, Stage.Scope, "scope:top") + self.bottom : StageCommandGroup = StageCommandGroup(self, Stage.BottomScope, "scope:bottom") + self.aux : StageCommandGroup = StageCommandGroup(self, Stage.AuxiliaryScope, "scope:aux") diff --git a/sentio_prober_control/Sentio/CommandGroups/StageCommandGroup.py b/sentio_prober_control/Sentio/CommandGroups/StageCommandGroup.py index 4a71283..6f6fd93 100644 --- a/sentio_prober_control/Sentio/CommandGroups/StageCommandGroup.py +++ b/sentio_prober_control/Sentio/CommandGroups/StageCommandGroup.py @@ -6,8 +6,9 @@ from typing import Optional + class StageCommandGroup(CommandGroupBase): - """This command group contains functions for working with motorized scopes. + """This command group contains functions for working with motorized stages. You are not meant to instantiate this class directly. Access it via the probe attribute of the [SentioProber](SentioProber.md) class. @@ -21,27 +22,11 @@ class StageCommandGroup(CommandGroupBase): ``` """ - def __init__(self, prober: 'SentioProber', stage : Stage, has_subgroups = False) -> None: # type: ignore + def __init__(self, prober: 'SentioProber', stage : Stage, stage_selector : str) -> None: # type: ignore super().__init__(prober) - self.__stage_selector: str = "" self.__stage = stage - - if stage==Stage.Scope: - self.__stage_selector = "scope:top" - elif stage==Stage.BottomScope: - self.__stage_selector = "scope:bottom" - elif stage==Stage.AuxiliaryScope: - self.__stage_selector = "scope:aux" - elif stage==Stage.Chuck: - self.__stage_selector = "chuck" - else: - raise ValueError(f"Invalid stage {stage} for ScopeCommandGroup") - - if stage==Stage.Scope and has_subgroups: - self.top: StageCommandGroup = StageCommandGroup(prober, Stage.Scope, False) - self.bottom: StageCommandGroup = StageCommandGroup(prober, Stage.BottomScope, False) - self.aux: StageCommandGroup = StageCommandGroup(prober, Stage.AuxiliaryScope, False) + self.__stage_selector = stage_selector def get_home(self, chuck_site : ChuckSite = ChuckSite.Wafer) -> Tuple[float, float, ChuckSite]: @@ -257,4 +242,8 @@ def step_site_next(self) -> Tuple[str, float, float]: @property def stage(self) -> Stage: """The stage this command group is for.""" - return self.__stage \ No newline at end of file + return self.__stage + + + + diff --git a/sentio_prober_control/Sentio/Compatibility.py b/sentio_prober_control/Sentio/Compatibility.py index b5b8a2a..7ecfba0 100644 --- a/sentio_prober_control/Sentio/Compatibility.py +++ b/sentio_prober_control/Sentio/Compatibility.py @@ -8,9 +8,10 @@ class CompatibilityLevel(IntEnum): 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, + Auto = 0 + Sentio_24 = 1 Sentio_25_2 = 2 + Experimental = 99 class Compatibility: @@ -22,7 +23,7 @@ class Compatibility: """ # Default compatibility level is Undefined - level : CompatibilityLevel = CompatibilityLevel.Undefined + level : CompatibilityLevel = CompatibilityLevel.Auto @staticmethod def assert_min(level : CompatibilityLevel) -> None: diff --git a/sentio_prober_control/Sentio/Enumerations.py b/sentio_prober_control/Sentio/Enumerations.py index 1f07cea..ad7cca3 100644 --- a/sentio_prober_control/Sentio/Enumerations.py +++ b/sentio_prober_control/Sentio/Enumerations.py @@ -2048,8 +2048,8 @@ class ZReference(Enum): Ready = 7 RealPos = 8 - def to_string(self, compat_level : CompatibilityLevel = CompatibilityLevel.Undefined) -> str: - if compat_level == CompatibilityLevel.Undefined: + def to_string(self, compat_level : CompatibilityLevel = CompatibilityLevel.Auto) -> str: + if compat_level == CompatibilityLevel.Auto: compat_level = Compatibility.level if compat_level < CompatibilityLevel.Sentio_25_2: diff --git a/sentio_prober_control/Sentio/ProberSentio.py b/sentio_prober_control/Sentio/ProberSentio.py index 7895a8e..913229a 100644 --- a/sentio_prober_control/Sentio/ProberSentio.py +++ b/sentio_prober_control/Sentio/ProberSentio.py @@ -46,6 +46,7 @@ from sentio_prober_control.Sentio.CommandGroups.WafermapCommandGroup import WafermapCommandGroup from sentio_prober_control.Sentio.CommandGroups.SetupCommandGroup import SetupCommandGroup from sentio_prober_control.Sentio.CommandGroups.StageCommandGroup import StageCommandGroup +from sentio_prober_control.Sentio.CommandGroups.ScopeCommandGroup import ScopeCommandGroup class SentioCommunicationType(Enum): @@ -79,7 +80,7 @@ class SentioProber(ProberBase): vision (VisionCommandGroup): The vision command group provides access to the vision modules functionality. """ - def __init__(self, comm: CommunicatorBase): + def __init__(self, comm: CommunicatorBase, compat_level : CompatibilityLevel = CompatibilityLevel.Auto) -> None: """Construct a SENTIO prober object. The prober must be initialized with a communication object that @@ -87,6 +88,7 @@ def __init__(self, comm: CommunicatorBase): Args: comm (CommunicatorBase): The communicator to use for communication with the prober. + compat_level (CompatibilityLevel): The compatibility level to use. If CompatibilityLevel.Auto is set SENTIO is queried to figure the compatibility level out. """ ProberBase.__init__(self, comm) @@ -94,7 +96,6 @@ def __init__(self, comm: CommunicatorBase): self.aux: AuxCommandGroup = AuxCommandGroup(self) self.loader: LoaderCommandGroup = LoaderCommandGroup(self) self.map: WafermapCommandGroup = WafermapCommandGroup(self) - self.probe: ProbeCommandGroup = ProbeCommandGroup(self) self.qalibria: QAlibriaCommandGroup = QAlibriaCommandGroup(self) self.service: ServiceCommandGroup = ServiceCommandGroup(self) self.siph: SiPHCommandGroup = SiPHCommandGroup(self) @@ -105,31 +106,35 @@ def __init__(self, comm: CommunicatorBase): self.__name = "SentioProber" self.comm.send("*RCS 1") # switch to the native SENTIO remote command set - version : str = self.status.get_version() - - # Extract version string - match = re.search(r"Version:\s*([\d\.]+)", version) - if match: - version = match.group(1) - 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: - Compatibility.level = CompatibilityLevel.Sentio_24 - elif major==25 and (minor==2 or (minor==1 and release==99)): - Compatibility.level = CompatibilityLevel.Sentio_25_2 - else: - Compatibility.level = CompatibilityLevel.Undefined + # If the compatibility Level is set to Auto, we will try to determine the compatibility level + if compat_level == CompatibilityLevel.Auto: + version : str = self.status.get_version() + + # Extract version string + match = re.search(r"Version:\s*([\d\.]+)", version) + if match: + version = match.group(1) + 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==25 and (minor==2 or (minor==1 and release==99)): + Compatibility.level = CompatibilityLevel.Sentio_25_2 + else: + Compatibility.level = CompatibilityLevel.Sentio_24 # # More command groups not supported by all SENTIO versions. # + + # The probe command group has optional sub-groups for top and bottom probes. + # These are only available for Sentio > 25.2 + self.probe: ProbeCommandGroup = ProbeCommandGroup(self) # 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) + self.scope: ScopeCommandGroup = ScopeCommandGroup(self, Stage.Scope, "scope:top") + self.chuck: StageCommandGroup = StageCommandGroup(self, Stage.Chuck, "chuck") # # Deprecated command groups