def calculateFocus(child, scale=1.7, OFFSET_Y=350): DEVICE_VIEW_WIDTH = 1850 DEVICE_VIEW_HEIGHT = 1080 ZOOM_VIEW_WIDTH = 1920 ZOOM_VIEW_HEIGHT = 1080 # Device percent coordinates percentX = child.position.x percentY = child.position.y # Convert to device view pixels devicePixelX = percentX * DEVICE_VIEW_WIDTH devicePixelY = percentY * DEVICE_VIEW_HEIGHT # Scale to zoom view zoomPixelX = devicePixelX * (ZOOM_VIEW_WIDTH / DEVICE_VIEW_WIDTH) zoomPixelY = devicePixelY * (ZOOM_VIEW_HEIGHT / DEVICE_VIEW_HEIGHT) # Device width scaling deviceWidthPixels = child.position.width * DEVICE_VIEW_WIDTH zoomDeviceWidth = deviceWidthPixels * (ZOOM_VIEW_WIDTH / DEVICE_VIEW_WIDTH) # X offset (account for width) dx = (ZOOM_VIEW_WIDTH / 2 - zoomPixelX) - (zoomDeviceWidth / 2) # Rotation-aware vertical offset rotation = getattr(child.position.rotate, "angle", "0deg") if rotation == "90deg" and percentY> 0.3: adjustedOffsetY = zoomDeviceWidth else: adjustedOffsetY = OFFSET_Y # Dynamic Y offset zones if percentY < 0.5: dy = (zoomPixelY - ZOOM_VIEW_HEIGHT / 2) + adjustedOffsetY elif 0.5 <= percentY < 0.6: dy = (zoomPixelY - ZOOM_VIEW_HEIGHT / 2) else: dy = (zoomPixelY - ZOOM_VIEW_HEIGHT / 2) - adjustedOffsetY if deviceWidthPixels > 1200 : scale = 1.8 dy -= 100 return {"x": dx, "y": dy, "scale": scale} def deviceType(self, path, props): try: docked_view = "Docked-East-" devices = [] tags = [] prop = props[0] if "Conveyor" in path: docked_view += "Conv" autStand.devices.build_device_mapping(prop) devices = autStand.devices.build_device_table(self) elif "VFD" in path: docked_view += "VFD" else: docked_view += "Device" tags = autStand.devices.getAllTags(self, prop) return [docked_view, tags, devices] except Exception as e: import traceback msg = "Error in deviceType: {}\n{}".format(str(e), traceback.format_exc()) system.perspective.print(msg) return None def handleTagHighlight(view, currentValue): tagAndPriority = str(currentValue.value or "") container = view.rootContainer.getChildren()[0] # --- CASE 1: Remove all highlights by applying CLEAR class --- if tagAndPriority.upper() == "CLEAR": for child in container.getChildren(): try: currentClasses = child.props.style['classes'].split(" ") filtered = [c for c in currentClasses if not c.startswith("Highlight/")] child.props.style.classes = " ".join(filtered) child.props.params.highlight = "" except: pass return False if "||" not in tagAndPriority: return parts = tagAndPriority.split("||") tag = parts[0] splitedTag = tag.split("/") deviceName = splitedTag[-1] # --- CASE 2: Open camera popup if "Camera" in deviceName: cameraView = container.getChild(deviceName) ipAddress = cameraView.props.get("params", {}).get("ipaddress", "") system.perspective.openPopup("kxYYzZ2O", "autStand/PopUp-Views/Camera", params = {"ipaddress": ipAddress}, title = deviceName) return components = container.getChildren() priority = parts[1] foundMatch = False # clear all highlights and apply new one when found for child in components: params = child.props.get("params", {}) tagProps = params.get("tagProps", {}) tagsList = list(tagProps) if len(tagsList) == 0: continue # child.props.style.classes = "" currentClasses = child.props.style.get('classes', '').split(" ") # strip only highlight-related classes filtered = [c for c in currentClasses if not c.startswith("Highlight/")] child.props.style.classes = " ".join(filtered) child.props.params.highlight = "" tagPath = tagsList[0] if tag == tagPath: path = child.props.get("path") device = str(path).split("/")[-1].lower() child.props.params.highlight = priority if "photoeye" not in device and not device.startswith("conveyor_"): child.props.style.classes += " Highlight/Pulse-" + priority docked_view = deviceType(view, path, tagProps) system.perspective.openDock(docked_view[0], params = {'tagProps':tagProps, 'tags': docked_view[1], 'devices':docked_view[2]}) system.perspective.sendMessage( "focusDevice", payload = calculateFocus(child), scope="session" ) foundMatch = True return foundMatch