import tempfile import threading import time from unittest import TestCase from station.control_service import StationService class Gateway: active_arm='89' def __init__(self): self.started=None;self.requests=[] def graph(self): return {'nodes':[],'topics':[],'services':['/station/grippers/arm_89/execute']} def states(self): elapsed=time.monotonic()-self.started if self.started else 0 return {},{'89':{'connected':True,'moving':bool(self.started and elapsed<.25), 'grip_state':0 if self.started and elapsed<.25 else 1,'permille':500.}} def call(self,typ,name,request,event,timeout): self.requests.append(request);self.started=time.monotonic() return type('Reply',(),{'success':True,'message':'accepted'})() def close(self): pass class GripperCommandsTest(TestCase): def test_waits_for_physical_completion_and_rejects_bounds(self): gateway=Gateway() with tempfile.TemporaryDirectory() as root: service=StationService(root=root,gateway=gateway) try: arm=service.arms['89'];start=time.monotonic() result=service._gripper('gripper_tune',arm,{'kind':'pos','pos':500,'force':33,'speed':25},threading.Event()) self.assertTrue(result['ok']);self.assertGreater(time.monotonic()-start,.2) self.assertEqual(gateway.requests[0].command.position,arm['stroke_mm']/2) count=len(gateway.requests) with self.assertRaises(ValueError): service._gripper('gripper_tune',arm,{'kind':'pos','pos':500,'force':999,'speed':25},threading.Event()) self.assertEqual(len(gateway.requests),count) finally: service.close() def test_configuration_changes_never_retarget_commands(self): from pathlib import Path gateway=Gateway() with tempfile.TemporaryDirectory() as root: service=StationService(root=root,gateway=gateway) try: path=Path(root)/'site.yaml';path.write_text('robot: before') service.config_path=path;service._config_signature=service._signature() path.write_text('robot: different-device') with self.assertRaisesRegex(RuntimeError,'配置已变化'): service.submit('gripper_probe','89') self.assertEqual(gateway.requests,[]) self.assertTrue(service._frozen_config.is_file()) finally: service.close()