diff --git a/README.md b/README.md index 346d8e5a24051ba60a43b4efedbb80b2b892b140..b2358f3e3ff9dde79b8b04e026729370a714025f 100644 --- a/README.md +++ b/README.md @@ -49,3 +49,11 @@ Funded by the Deutsche Forschungsgemeinschaft (DFG, German Research Foundation) Funded by the Deutsche Forschungsgemeinschaft (DFG, German Research Foundation) under Project-ID 432233186 -- AIMS. +## License of used third-party libraries + +| Library | License | +|----------|-----------------------------| +| numpy | BSD License | +| requests | BSD License | +| toml | MIT License | +| wzl-udi | MIT License | \ No newline at end of file diff --git a/assets/interfaces/cartCMM.soil b/assets/interfaces/cartCMM.soil new file mode 100644 index 0000000000000000000000000000000000000000..2a4ce6cdae95a1fbd8725b4777663998bf925c40 --- /dev/null +++ b/assets/interfaces/cartCMM.soil @@ -0,0 +1,29 @@ +import tool; +import toolChanger; +import part; + +enum CoordinateSystems { + MachineCsy + MovableMachineCsy + MultipleArmCsy + RotaryTableVarCsy + PartCsy +} + +variable ActiveCsy { + name: "ActiveCsy" + description: "The currently active coordinate system." + datatype: enum + dimension: [] + range: CoordinateSystems +} + +component CartCMM { + name: "CartCMM" + description: "Cartesian Coordinate Measuring Machine" + components: + toolChanger.ToolChanger toolChanger + part.Part part + parameters: + tool.Id activeTool +} \ No newline at end of file diff --git a/assets/interfaces/ipp.soil b/assets/interfaces/ipp.soil new file mode 100644 index 0000000000000000000000000000000000000000..87ce326409fad5c25f88017be36bd03ece30e1bc --- /dev/null +++ b/assets/interfaces/ipp.soil @@ -0,0 +1,23 @@ +import cartCMM; + +function AbortE { + name: "AbortE" + description: "Aborts all pending transactions and stops the machine from moving." +} + +function ClearAllErrors { + name: "ClearAllErrors" + description: "Is called to recover from an error." +} + +component Ipp { + name: "I++" + description: "The top-level component of the Server." + components: + cartCMM.CartCMM cmm + functions: + AbortE abortE + ClearAllErrors clearAllErrors +} + +interface Ipp IppServer {} \ No newline at end of file diff --git a/assets/interfaces/part.soil b/assets/interfaces/part.soil new file mode 100644 index 0000000000000000000000000000000000000000..05ac5037da2cdd900367b1fbf14ebc3b46622087 --- /dev/null +++ b/assets/interfaces/part.soil @@ -0,0 +1,29 @@ +@prefix quantitykind: <http://qudt.org/vocab/quantitykind/> ; +@prefix unit: <http://qudt.org/vocab/unit/> ; + +variable Temperature defines <quantitykind:Temperature> { + name: "Temperature" + description: "Current temperature of the part." + datatype: float + dimension: [] + range: (-50, 200) + unit: <unit:DEG_C> +} + +variable Approach defines <quantitykind:Distance>{ + name: "Approach" + description: "Currently specified approach distance of the part." + datatype: float + dimension: [] + range: (0, 500) + unit: <unit:MilliM> +} + +component Part { + name: "Part" + description: "Soil representation of a Part." + measurements: + Temperature temperature + parameters: + Approach approach +} \ No newline at end of file diff --git a/assets/interfaces/tool.soil b/assets/interfaces/tool.soil new file mode 100644 index 0000000000000000000000000000000000000000..0c24d70705184bc4f9c033c0d13d2a13b02478af --- /dev/null +++ b/assets/interfaces/tool.soil @@ -0,0 +1,59 @@ +@prefix quantitykind: <http://qudt.org/vocab/quantitykind/> ; +@prefix unit: <http://qudt.org/vocab/unit/> ; + +variable Name { + name: "Name" + description: "Name of a Tool" + datatype: string + dimension: [] +} + +variable Id { + name: "Id" + description: "Id of a Tool" + datatype: string + dimension: [] +} + +# variable PositionList { +# name: "PositionList" +# description: "List of Points used for ScanOnCurve" +# datatype: float +# dimension: [0, 3] +# } + +variable Position defines <quantitykind:CartesianCoordinates> { + name: "Position" + description: "Current position of the tool, consisting of the I++ X, Y and Z values." + datatype: float + dimension: [3] + unit: <unit:MilliM> +} + +function ScanOnCurve { + name: "ScanOnCurve" + description: "Function that issues a ScanOnCurve command on the I++ Server" + arguments: + PositionList posL +} + +function PtMeas { + name: "PtMeas" + description: "Function that issues a PtMeas command on the I++ Server" + arguments: + Position pos +} + +component Tool { + name: "Tool" + description: "The currently active Tool." + measurements: + Position pos + parameters: + constant Name name + constant Id id + PositionList posL + functions: + PtMeas ptMeas + ScanOnCurve scanOnCurve +} \ No newline at end of file diff --git a/assets/interfaces/toolChanger.soil b/assets/interfaces/toolChanger.soil new file mode 100644 index 0000000000000000000000000000000000000000..f2859ed56e833b071bae31bd5033f795078fe569 --- /dev/null +++ b/assets/interfaces/toolChanger.soil @@ -0,0 +1,23 @@ +import tool; + +function EnumTools { + name: "EnumTools" + description: "Function that issues an EnumTools command on the I++ server." +} + +function ChangeTool { + name: "EnumTools" + description: "Function that issues an ChangeTool command on the I++ server." + arguments: + tool.Id tool_id +} + +component ToolChanger { + name: "ToolChanger" + description: "ToolChanger component" + components: + dynamic tool.Tool tools + functions: + EnumTools enumTools + ChangeTool changeTool +} \ No newline at end of file diff --git a/init.bat b/init.bat index b412275364f36b4d5a0e93b667be2916c7a58c75..2821a3da30811c65263e1dc294f8e1e1b9e7eee4 100644 --- a/init.bat +++ b/init.bat @@ -1,4 +1,5 @@ cd scripts +:: python generate.py ipp.soil -g shell -s -hwc python generate.py robot.soil -g remote -s -hwc python generate.py lasertracker.soil -g remote -s -hwc python generate.py monitoring.soil -g remote -s -hwc diff --git a/requirements.txt b/requirements.txt index afee9be188c475d4ac141a122018f663c83093a3..4b8519b2e5c54433e47d7f91686c00cc0e5c13af 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -numpy -requests -toml -wzl-udi==9.3.8 +numpy==1.24.2 +requests==0.10.2 +toml== +wzl-udi==10.0.0 diff --git a/requirements_dev.txt b/requirements_dev.txt index f80a93443af2e7c02ebb6dfaaceebffb50b3e932..2092c7c79057ad3675960608a936b25de4e69dc6 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -2,7 +2,7 @@ numpy requests toml tqdm -wzl-udi==9.3.3 +wzl-udi==10.0.0 kaleido plotly pandas \ No newline at end of file diff --git a/scripts/generate.py b/scripts/generate.py index 70aeb9fa48f794a8a378d4d7b2e2d7da3d35bc54..7aaf085ca77176fdc446655801866f367d3ca4e7 100644 --- a/scripts/generate.py +++ b/scripts/generate.py @@ -71,6 +71,6 @@ if __name__ == '__main__': stderr=subprocess.PIPE) stdout, stderr = process.communicate() print(stdout.decode()) - print(stderr.decode()) + print(stderr.decode('latin-1')) else: print(f'Generator type "{args.generator}" is unknown, must be one of "shell", "local", "remote".') diff --git a/src/assessment.py b/src/assessment.py index db1542fb3f08d4cd39fee86b239ee993c713fef9..ff7ba1c5a56bc98b7eeeb58bf83cb2b03986d2d1 100644 --- a/src/assessment.py +++ b/src/assessment.py @@ -9,7 +9,7 @@ from assessment.framework import Framework, AssessmentError, FUJI, FAIRChecker SRC = os.path.join('.') OUT = os.path.join('..', 'out') -PROFILES = True +PROFILES = False METADATA = True diff --git a/src/evaluation.py b/src/evaluation.py index 177a41880aa1327801ab2ec810849dbd9d273e3c..241b83fe5c89222b7a43fdbed9c08872c03950bc 100644 --- a/src/evaluation.py +++ b/src/evaluation.py @@ -8,14 +8,14 @@ SRC = os.path.join('..', 'src') ASSESSMENTS = os.path.join('..', 'out') OUT = os.path.join('..', 'out') -PROFILES = True +PROFILES = False METADATA = True if __name__ == '__main__': dummies = ['lasertracker', 'monitoring', 'robot'] profile_scores = pandas.DataFrame() - metadata_scores = {'f-uji': [], 'fair-checker': []} + metadata_scores = pandas.DataFrame(columns=["framework", "dummy", "principle", "value"]) for dummy in dummies: @@ -26,15 +26,18 @@ if __name__ == '__main__': for framework in frameworks: if PROFILES: - profile_scores[framework.name] += [ - framework.evaluate(os.path.join(ASSESSMENTS, framework.name, dummy, 'profiles'))] + framework.evaluate(os.path.join(ASSESSMENTS, framework.name, dummy, 'profiles'), dummy) if METADATA: - metadata_scores[framework.name] += [ - framework.evaluate(os.path.join(ASSESSMENTS, framework.name, dummy, 'profiles'))] + score = framework.evaluate(os.path.join(ASSESSMENTS, framework.name, dummy, 'metadata'), dummy) + metadata_scores = pandas.concat([metadata_scores, score], ignore_index=True) - average_profile_scores = [profile_scores[key].mean() for key in profile_scores] - average_metadata_scores = [metadata_scores[key].mean() for key in metadata_scores] + average_scores = metadata_scores.groupby(['framework', 'principle'], as_index=False).agg({'value': 'mean'}) - Framework.visualize(average_profile_scores, list(profile_scores.keys()), 'profiles.png') - Framework.visualize(average_metadata_scores, list(profile_scores.keys()), 'metadata.png') + print(average_scores) + + # average_profile_scores = [profile_scores[key].mean() for key in profile_scores] + # average_metadata_scores = [metadata_scores[key].mean() for key in metadata_scores] + + # Framework.visualize(average_profile_scores, list(profile_scores.keys()), 'profiles.png') + Framework.visualize(metadata_scores, 'metadata.png') diff --git a/src/ipp/hwc/__init__.py b/src/ipp/hwc/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/ipp/hwc/com_tool.py b/src/ipp/hwc/com_tool.py new file mode 100644 index 0000000000000000000000000000000000000000..200433381844ebc2d0cda5b62e7c0afb39ad7392 --- /dev/null +++ b/src/ipp/hwc/com_tool.py @@ -0,0 +1,17 @@ +from scripts.gen.ipp.tool.com_tool import COMToolTOP +from scripts.gen.ipp.hwc.device import Device + + +class COMTool(COMToolTOP): + def __init__(self, device: Device, name: str = "", id: str = "", posl: list[float] = None ): + COMToolTOP.__init__(self, device, name, id) + self._device = device + + def fun_ptmeas(self, arg_pos): + self._device.pt_meas(arg_pos[0], arg_pos[1], arg_pos[2]) + + def scanoncurve(self, posl): + self._device.scanoncurve(posl) + + def get_mea_pos(self): + return self._device.position diff --git a/src/ipp/hwc/device.py b/src/ipp/hwc/device.py new file mode 100644 index 0000000000000000000000000000000000000000..604cef5785c9293e615e3aa666c20117fa553571 --- /dev/null +++ b/src/ipp/hwc/device.py @@ -0,0 +1,55 @@ +import asyncio +import time +from scripts.gen.ipp.device import DeviceTOP +from threading import Thread +from . import ipp_client +import re + + +class Tool(object): + def __init__(self, position): + self._position = position + + def update(self, position): + self._position = position + + @property + def position(self): + return self._position + + +class Device(DeviceTOP, Thread): + def __init__(self): + DeviceTOP.__init__(self) + Thread.__init__(self) + self._x = 0 + self._y = 0 + self._z = 0 + self._tool = Tool(self.position) + self._error = False + self._ipp_client = None + + async def goto(self, x, y, z): + await self._ipp_client.transaction("GoTo(X(" + x + "), Y(" + y + "), Z(" + z + "))", False) + + def pt_meas(self, x, y, z): + self._ipp_client.transaction("PtMeas(X(" + str(x) + "), Y(" + str(y) + "), Z(" + str(z) + "))", False) + + @property + def position(self): + return [self._x, self._y, self._z] + + def run(self): + self._ipp_client = ipp_client.IPPClient(self) + self._ipp_client.start() + time.sleep(1) + self._ipp_client.transaction("StartSession()", False) + self._ipp_client.transaction("PtMeas(X(1), Y(2), Z(3))", False) + self._ipp_client.transaction("GoTo(X(1), Y(2), Z(3))", False) + for i in range(1000): + self._ipp_client.transaction("ABC(1,2,3)", False) + self._ipp_client.transaction("OnMoveReport(X(), Y(), Z())", True) + + def stop(self): + loop = asyncio.get_event_loop() + loop.run_until_complete(self._ipp_client.transaction("EndSession()", False)) diff --git a/src/ipp/hwc/ippServerConfig.toml b/src/ipp/hwc/ippServerConfig.toml new file mode 100644 index 0000000000000000000000000000000000000000..1f90202fec649f5c9f1364737f4b9b7b8d00e84e --- /dev/null +++ b/src/ipp/hwc/ippServerConfig.toml @@ -0,0 +1,5 @@ +dataformat = 'json' + +[tcp] + address='localhost' + port=1294 \ No newline at end of file diff --git a/src/ipp/hwc/ipp_client.py b/src/ipp/hwc/ipp_client.py new file mode 100644 index 0000000000000000000000000000000000000000..ccbf668ad7c7e3c1b6f9ad24644d959d9502db7b --- /dev/null +++ b/src/ipp/hwc/ipp_client.py @@ -0,0 +1,126 @@ +from __future__ import annotations +import socket +from threading import Thread +import typing +from . import transaction + +if typing.TYPE_CHECKING: + from .device import Device + + +class UnexpectedTagError(Exception): + def __init__(self): + self.message = "The received message contains an unexpected tag." + + +class MissingACKError(Exception): + def __init__(self): + self.message = "Was expecting an ACK response but received something else." + + +class MissingTransactionCompleteResponseError(Exception): + def __init__(self): + self.message = "Transaction complete response is missing." + + +class DaemonNonDataMessage(Exception): + def __init__(self): + self.message = "Received a daemon message that is not a data message!" + + +class IPPClient(Thread): + def __init__(self, device_i: Device): + Thread.__init__(self) + self._socket = None + self._active = False + self._command_tag = 1 + self._transaction_log = {} + self._data_log = {} + self._daemon_log = {} + self._active_streams = [] + self._device = device_i + + def _handle_event(self, tag, message_type, message_body): + non_event_tag = "0" + tag[1:5] + if non_event_tag in self._active_streams: + daemon = self._transaction_log.get(non_event_tag) + if message_type != "#": + raise DaemonNonDataMessage() + daemon['cb'].update_device(message_body.split(",")) + + + def _parse_message(self, message): + print("<-- " + message) + message = message.split(" ") + tag = message[0] + message_type = message[1] + if tag[0] == "E": + self._handle_event(tag, message_type, "".join(message[2:])) + return + + transaction = self._transaction_log.get(tag) + if not transaction['ACK']: + if message_type == '&': + transaction['ACK'] = True + return + else: + raise MissingACKError() + + if message_type == '#': + self._data_log[tag + '#' + transaction['uuid']].append(message[::2]) + return + + if message_type == '%': + if not transaction['daemon']: + self._transaction_log.pop(tag) + return + + return + + def _send(self, message): + print("--> " + message.decode()) + self._socket.sendall(message) + + def transaction(self, command, streams): + tag = str(self._command_tag).zfill(5) + command_name = command.split('(')[0] + + if streams: + self._active_streams.append(tag) + + # Add transaction to pending transaction log + self._transaction_log[tag] = { + 'ACK': False, + 'uuid': command, + 'daemon': streams, + 'cb': transaction.create(command_name, tag, self._device) + } + + self._send(str.encode(tag + " " + command + "\n")) + self._command_tag = self._command_tag + 1 + + def _listen(self): + buffer = [] + stitch = False + while True: + if (len(buffer) > 0 and not stitch) or (len(buffer) > 1): + self._parse_message(buffer[0]) + buffer.pop(0) + continue + data, _ = self._socket.recvfrom(1024) + data = data.decode() + buffer = buffer + (data.splitlines()) + if stitch: + buffer[0:2] = ["".join(buffer[0:2])] + stitch = False + if len(data) > 0: + if data[-1] != '\n': + stitch = True + + def run(self): + self._active = True + self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self._socket.connect(('localhost', 1294)) + + listener = Thread(target=self._listen) + listener.start() diff --git a/src/ipp/hwc/transaction.py b/src/ipp/hwc/transaction.py new file mode 100644 index 0000000000000000000000000000000000000000..82b67f64fc15bb31248ea1caa469e7b3d73c50e5 --- /dev/null +++ b/src/ipp/hwc/transaction.py @@ -0,0 +1,92 @@ +from __future__ import annotations +from abc import ABC, abstractmethod +import re +import typing +from sys import modules + +if typing.TYPE_CHECKING: + from .device import Device + + +class TransactionNotImplementedException(Exception): + def __init__(self): + self.message = "Tried to use an unimplemented Transaction." + + +class Transaction(ABC): + + def __init__(self, tag: str, device: Device): + self._tag = tag + self._device = device + + @property + def tag(self) -> str: + return self._tag + + @abstractmethod + def update_device(self, data): + ... + + +class OnMoveReportTransaction(Transaction): + def __init__(self, tag: str, device: Device): + Transaction.__init__(self, tag, device) + + def update_device(self, data): + print("OnMoveReport update: " + '[%s]' % ', '.join(map(str, data))) + for data_point in data: + axis = data_point.split("(")[0] + value = re.findall(r"-?\d+\.\d+", data_point) + value = value[0] + match axis: + case "X": + self._device._x = float(value) + case "Y": + self._device._y = float(value) + case "Z": + self._device._z = float(value) + # self._tool.update([self._x, self._y, self._z]) + + +class StartSessionTransaction(Transaction): + def __init__(self, tag: str, device: Device): + Transaction.__init__(self, tag, device) + + def update_device(self, data): + pass + + +class PtMeasTransaction(Transaction): + def __init__(self, tag: str, device: Device): + Transaction.__init__(self, tag, device) + + def update_device(self, data): + pass + + +class ScanOnCurveTransaction(Transaction): + def __init__(self, tag: str, device: Device): + Transaction.__init__(self, tag, device) + + def update_device(self, data): + pass + + +class GoToTransaction(Transaction): + def __init__(self, tag: str, device: Device): + Transaction.__init__(self, tag, device) + + def update_device(self, data): + pass + + +def create(command: str, tag: str, device: Device) -> Transaction | None: + module = modules[__name__] + try: + transaction = getattr(module, '{}Transaction'.format(command))(tag, device) + except Exception: + print("Unknown command: " + '"' + command + '"') + print("No callback registered.") + return None + return transaction + diff --git a/src/ipp/main.py b/src/ipp/main.py new file mode 100644 index 0000000000000000000000000000000000000000..25e0a0699d5f0fbe464c341bb3d98758d8d113d0 --- /dev/null +++ b/src/ipp/main.py @@ -0,0 +1,12 @@ +import toml + +import start +from hwc.device import Device +from com_ipp import COMIpp + +if __name__ == '__main__': + device = Device() + soilClient = COMIpp(device) + device.start() + + start.start(soilClient, toml.load('config.toml'), 'model.json') \ No newline at end of file diff --git a/src/lasertracker/hwc/com_base.py b/src/lasertracker/hwc/com_base.py index bd8dbfce26a9ae3bd29e6ee71739cdb184558cbb..478847c52e776955aa456a8e5f256d0584477d43 100644 --- a/src/lasertracker/hwc/com_base.py +++ b/src/lasertracker/hwc/com_base.py @@ -24,13 +24,13 @@ class COMBase(COMBaseTOP): self._device.point_to(*par_position) def get_mea_azimuth(self): - return self._device.azimuth + return self._device.azimuth, random.random() * 10e-10 def get_mea_elevation(self): - return self._device.elevation + return self._device.elevation, random.random() * 10e-10 def get_mea_distance(self): - return self._device.distance + return self._device.distance, random.random() * 10e-9 def get_par_state(self): return str(self._device.state) diff --git a/src/lasertracker/hwc/com_target.py b/src/lasertracker/hwc/com_target.py index 0c0d817b6272910927221a8c88c6d7059fe71216..cb0710f363c7dc078f33b2e3c0cbef39856c8ef7 100644 --- a/src/lasertracker/hwc/com_target.py +++ b/src/lasertracker/hwc/com_target.py @@ -1,3 +1,5 @@ +import random + import time from typing import List @@ -21,7 +23,7 @@ class COMTarget(COMTargetTOP): yield self.get_mea_position(), arg_label def get_mea_position(self): - return self._device.target.position + return self._device.target.position, [[random.random()*10e-9 for j in range(3)] for i in range(3)] def get_par_state(self): return str(self._device.target.state) diff --git a/src/monitoring/hwc/com_environmentalsensor.py b/src/monitoring/hwc/com_environmentalsensor.py index dc1bd5aae9c581defea49776011c74cd595e1dd9..ab7f19ab37252e03a1313bf8ce096f85ae5f9033 100644 --- a/src/monitoring/hwc/com_environmentalsensor.py +++ b/src/monitoring/hwc/com_environmentalsensor.py @@ -1,3 +1,7 @@ +from typing import Tuple, Any + +import random + from com_environmentalsensor import COMEnvironmentalSensorTOP from hwc.device import Device @@ -8,20 +12,20 @@ class COMEnvironmentalSensor(COMEnvironmentalSensorTOP): COMEnvironmentalSensorTOP.__init__(self, device, location) self.uuid = id - def get_mea_temperature(self) -> float: - return self._device.sensors[self.uuid].temperature + def get_mea_temperature(self) -> Tuple[float, Any]: + return self._device.sensors[self.uuid].temperature, random.random() * 10e-2 - def get_mea_pressure(self) -> float: - return self._device.sensors[self.uuid].pressure + def get_mea_pressure(self) -> Tuple[float, Any]: + return self._device.sensors[self.uuid].pressure, random.random() * 10e-1 - def get_mea_humidity(self) -> float: - return self._device.sensors[self.uuid].humidity + def get_mea_humidity(self) -> Tuple[float, Any]: + return self._device.sensors[self.uuid].humidity, random.random() * 10e-3 - def get_mea_signalstrength(self) -> int: - return self._device.sensors[self.uuid].signal_strength + def get_mea_signalstrength(self) -> Tuple[int, Any]: + return self._device.sensors[self.uuid].signal_strength, None - def get_mea_batterylevel(self) -> int: - return self._device.sensors[self.uuid].battery_level + def get_mea_batterylevel(self) -> Tuple[int, Any]: + return self._device.sensors[self.uuid].battery_level, None def get_par_location(self) -> str: return self._par_location diff --git a/src/robot/hwc/com_mobilerobot.py b/src/robot/hwc/com_mobilerobot.py index d428f154514221ca363fd5615136afb4237ef5cb..3665c28fd8bf3e89af80f59952297b02ca6abf79 100644 --- a/src/robot/hwc/com_mobilerobot.py +++ b/src/robot/hwc/com_mobilerobot.py @@ -1,4 +1,4 @@ -from typing import List +from typing import List, Any, Tuple import numpy @@ -13,11 +13,11 @@ class COMMobileRobot(COMMobileRobotTOP): self._mea_position = numpy.array([0, 0, 0], dtype=float) self._device.start() - def get_mea_position(self) -> List[float]: - return self._device.position.tolist() + def get_mea_position(self) -> Tuple[List[float], Any]: + return self._device.position.tolist(), None - def get_mea_batterylevel(self) -> int: - return self._device.batterylevel + def get_mea_batterylevel(self) -> Tuple[int, Any]: + return self._device.batterylevel, None def get_par_auto(self) -> bool: return self._device.mode == Mode.AUTO diff --git a/src/robot/hwc/com_robot.py b/src/robot/hwc/com_robot.py index 62cfdd3588df27e8ea1ff77a00317c5804ee8358..db212f07366b10bbaf1a80a1d0def50e92b2456f 100644 --- a/src/robot/hwc/com_robot.py +++ b/src/robot/hwc/com_robot.py @@ -1,5 +1,5 @@ import time -from typing import List +from typing import List, Tuple, Any import numpy @@ -16,8 +16,8 @@ class COMRobot(COMRobotTOP): self._mea_position = numpy.array([0, 0, 0], dtype=float) self._com_gripper = COMGripper(device, open=True) - def get_mea_position(self) -> List[float]: - return self._device.position.tolist() + def get_mea_position(self) -> Tuple[List[float], Any]: + return self._device.position.tolist(), None def fun_goto(self, arg_position: List[float] = None): self._device.goto(numpy.array(arg_position))