def calculateFocus(self, child, scale=1.7): """ Please don't modify! Hybrid focusing: - MCM01: precise per-rotation affine for (dx, dy) - MCM02: search-style mapping: * 0°/180°: strong horizontal (dx from x,y), dy very gentle (clamped) * 90°/270°: dx = 0, dy from x left/right anchors) """ # --- rotation --- try: rot = int(str(self.session.custom.rotation).replace("deg", "")) % 360 except: rot = 0 # --- child coords --- try: x = float(child.position.x) y = float(child.position.y) w = float(child.position.width) except: system.perspective.print("Invalid position data on child component.") return {"x": 0, "y": 0, "scale": scale} # --- zone --- try: zone = child.props.params.tagProps[0].split("/")[1] except: zone = "MCM01" # ======== MCM01 ======== COEFFS_MCM01 = { 0: dict(ax= 956.86984, bx=-1853.94329, cx= -17.57191, ay= 124.82245, by= -191.28916, cy= 227.58568), 90: dict(ax=-601.58230, bx= -218.90739, cx=1466.46475, ay= 284.67701, by=-1528.43884, cy= 54.37458), 180: dict(ax=-728.83646, bx= 1521.61995, cx= 16.04437, ay=-434.03405, by= -32.67146, cy=1071.85472), 270: dict(ax= 385.53372, bx= -44.67850, cx=-768.65879, ay=-1341.88064, by= 1615.55073, cy= 142.77638), } # ======== MCM02 ======== # 0°: dx ≈ a + b*x + c*y _M2_DX_0 = dict(a= 691.72633028, b=-1788.01433165, c= 295.57517840) # dy kept light & clamped around center (use a small gain around 0.5) def _m2_dy0(y): dy = 320.0 * (0.5 - y) # gentle bias return max(min(dy, 120.0), -120.0) # 180°: dx ≈ a + b*x + c*y _M2_DX_180 = dict(a=-730.66959977, b= 1680.89494266, c= -70.52210472) def _m2_dy180(y): dy = 200.0 * (y - 0.5) # tiny bias return max(min(dy, 80.0), -80.0) # 90°: dx = 0, dy ≈ A + B*x (two-point fit from left/right; matches your sheet well) _M2_DY_90_A = 278.8 _M2_DY_90_B = -1663.6 # ~ from (left 215.88) → (right -1213.52) # 270°: dx = 0, dy ≈ A + B*x _M2_DY_270_A = -1284.8 _M2_DY_270_B = 1686.1 # ~ from (left -1221.17) → (right 227.06) # ======== compute (dx, dy) ======== if zone == "MCM01": c = COEFFS_MCM01.get(rot, COEFFS_MCM01[0]) dx = c["ax"] + c["bx"] * x + c["cx"] * y dy = c["ay"] + c["by"] * x + c["cy"] * y else: # ------- MCM02 (search-style) ------- if rot == 0: dx = _M2_DX_0["a"] + _M2_DX_0["b"] * x + _M2_DX_0["c"] * y dy = _m2_dy0(y) elif rot == 180: dx = _M2_DX_180["a"] + _M2_DX_180["b"] * x + _M2_DX_180["c"] * y dy = _m2_dy180(y) elif rot == 90: dx = 0.0 dy = _M2_DY_90_A + _M2_DY_90_B * x elif rot == 270: dx = 0.0 dy = _M2_DY_270_A + _M2_DY_270_B * x else: dx, dy = 0.0, 0.0 # --- slight downward shift for flat rotations --- if rot in (0, 180): dy -=50 # ======== wide device tweak (unchanged) ======== try: deviceWidthPixels = float(w) * 1850.0 if deviceWidthPixels > 1200: scale = max(scale, 1.8) dy -= 100 except: pass return {"x": dx, "y": dy, "scale": scale} def deviceType(self, path, props): try: docked_view = "Docked-East-" section = "all" devices = [] tags = [] prop = props[0] # --- VFD --- if "VFD" in path: docked_view += "VFD" section = "vfd" # --- Conveyor --- elif "Conv" in path or "Conveyor" in path: docked_view += "Conv" autStand.devices.build_device_mapping(prop) devices = autStand.devices.build_device_table(self) section = "conveyor" # --- Generic devices --- else: docked_view += "Device" tags = autStand.devices.getAllTags(self, prop, section=section) 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 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: props = child.meta.name 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], 'name':props}) system.perspective.sendMessage( "focusDevice", payload = calculateFocus(view, child), scope="session" ) foundMatch = True return foundMatch