import sys
import os
from contextlib import contextmanager
from robodk.robolink import Robolink, Item, ITEM_TYPE_ROBOT
from robodk import robomath
from typing import List, Dict, Optional, Any
@contextmanager
def suppress_stdout():
"""Redirects stdout to stderr to prevent contaminating JSON-RPC stream."""
original_stdout = sys.stdout
sys.stdout = sys.stderr
try:
yield
finally:
sys.stdout = original_stdout
class RoboDKService:
def __init__(self):
self._rdk: Optional[Robolink] = None
def connect(self) -> str:
"""Connects to RoboDK, starting it if necessary."""
with suppress_stdout():
if self._rdk is None:
self._rdk = Robolink() # API to communicate with RoboDK
# Attempt to connect/check connection.
# Connect() matches checking status or establishing it.
# Safe to call repeatedly.
self._rdk.Connect()
# Ensure UI matches (Render(True) might not be needed always but good for sync)
try:
self._rdk.Render(True)
except:
pass # Ignore render errors
# Just return a session ID placeholder
return "default_session"
def open_station(self, file_path: Optional[str] = None, empty: bool = False) -> None:
"""Opens a station or creates a new one."""
self.connect()
with suppress_stdout():
if empty:
self._rdk.CloseStation() # Close current station
self._rdk.AddStation("New Station")
elif file_path:
self._rdk.AddFile(file_path)
def add_robot(self, robot_model: str) -> Dict[str, str]:
"""Adds a robot to the active station."""
self.connect()
with suppress_stdout():
item = self._rdk.AddFile(robot_model)
if not item.Valid():
raise ValueError(f"Could not load robot: {robot_model}")
return {
"id": item.Name(),
"name": item.Name()
}
def get_station_tree(self) -> List[Dict[str, Any]]:
"""Returns the tree of the current station."""
self.connect()
with suppress_stdout():
items = self._rdk.ItemList()
tree_nodes = []
# Processing items shouldn't print, but listing them might
for item in items:
try:
# Name(), Type(), Parent() are generally safe but simple accessors
name = item.Name()
item_type_id = item.Type()
parent = item.Parent()
parent_id = parent.Name() if parent.Valid() else None
type_map = {
1: "station",
2: "robot",
3: "frame",
4: "tool",
5: "object",
6: "target",
8: "program"
}
type_str = type_map.get(item_type_id, "unknown")
tree_nodes.append({
"id": name,
"name": name,
"type": type_str,
"parent": parent_id
})
except Exception:
continue
return tree_nodes