From 92574c134180ac04b724efdfe98000aa2450db08 Mon Sep 17 00:00:00 2001 From: gac-S_Changer Date: Wed, 7 Nov 2018 11:36:38 +0100 Subject: [PATCH] --- config/devices.properties | 2 +- devices/led_ctrl_1.properties | 4 +- devices/led_ctrl_2.properties | 4 +- devices/led_ctrl_3.properties | 4 +- devices/smart_magnet.properties | 4 +- script/client/PShellClient.py | 369 ++++++++++++++++++++++++++++++++ script/client/TellClient.py | 167 +++++++++++++++ script/client/tell.py | 42 ++++ script/data/samples.py | 4 +- script/devices/Hexiposi.py | 80 ++++--- script/devices/RobotSC.py | 3 +- script/devices/RobotTCP.py | 27 ++- script/devices/SmartMagnet.py | 10 +- script/local.py | 17 +- script/motion/calibrate_tool.py | 2 +- script/motion/dry.py | 7 +- script/motion/mount.py | 13 +- script/motion/move_cold.py | 4 + script/motion/put_gonio.py | 5 + script/motion/scan_pin.py | 4 + script/motion/trash.py | 4 + script/motion/unmount.py | 6 + script/test/TestCalib.py | 30 +++ 23 files changed, 749 insertions(+), 63 deletions(-) create mode 100644 script/client/PShellClient.py create mode 100644 script/client/TellClient.py create mode 100644 script/client/tell.py create mode 100644 script/test/TestCalib.py diff --git a/config/devices.properties b/config/devices.properties index f9ed729..02dfaa7 100644 --- a/config/devices.properties +++ b/config/devices.properties @@ -1,4 +1,4 @@ -img=ch.psi.pshell.prosilica.Prosilica|25001 "PacketSize=1522;PixelFormat=Mono8;BinningX=1;BinningY=1;RegionX=300;RegionY=200;Width=1000;Height=1000;MulticastEnable=Off"|||false +#img=ch.psi.pshell.prosilica.Prosilica|25001 "PacketSize=1522;PixelFormat=Mono8;BinningX=1;BinningY=1;RegionX=300;RegionY=200;Width=1000;Height=1000;MulticastEnable=Off"|||false gripper_cam=ch.psi.pshell.imaging.MjpegSource|http://axis-accc8e9cc87b.psi.ch/axis-cgi/mjpg/video.cgi?camera=1 reopen||-200| monitoring_cam=ch.psi.pshell.imaging.MjpegSource|http://axis-accc8e9cc87b.psi.ch/axis-cgi/mjpg/video.cgi?camera=2 reopen||-200| top_cam=ch.psi.pshell.imaging.MjpegSource|http://axis-accc8e9cc87b.psi.ch/axis-cgi/mjpg/video.cgi?camera=3 true||-200| diff --git a/devices/led_ctrl_1.properties b/devices/led_ctrl_1.properties index c4fe54c..9f3734a 100644 --- a/devices/led_ctrl_1.properties +++ b/devices/led_ctrl_1.properties @@ -1,5 +1,5 @@ -#Mon Oct 15 11:33:41 CEST 2018 -maxValue=1.0 +#Thu Oct 25 15:44:44 CEST 2018 +maxValue=0.4 minValue=0.0 offset=0.0 precision=2 diff --git a/devices/led_ctrl_2.properties b/devices/led_ctrl_2.properties index c4fe54c..9f3734a 100644 --- a/devices/led_ctrl_2.properties +++ b/devices/led_ctrl_2.properties @@ -1,5 +1,5 @@ -#Mon Oct 15 11:33:41 CEST 2018 -maxValue=1.0 +#Thu Oct 25 15:44:44 CEST 2018 +maxValue=0.4 minValue=0.0 offset=0.0 precision=2 diff --git a/devices/led_ctrl_3.properties b/devices/led_ctrl_3.properties index c4fe54c..9f3734a 100644 --- a/devices/led_ctrl_3.properties +++ b/devices/led_ctrl_3.properties @@ -1,5 +1,5 @@ -#Mon Oct 15 11:33:41 CEST 2018 -maxValue=1.0 +#Thu Oct 25 15:44:44 CEST 2018 +maxValue=0.4 minValue=0.0 offset=0.0 precision=2 diff --git a/devices/smart_magnet.properties b/devices/smart_magnet.properties index 12bff22..012c86e 100644 --- a/devices/smart_magnet.properties +++ b/devices/smart_magnet.properties @@ -1,6 +1,8 @@ -#Wed Oct 17 16:51:06 CEST 2018 +#Thu Oct 25 16:47:21 CEST 2018 holdingCurrent=30.0 mountCurrent=10.0 remanenceCurrent=-10.0 restingCurrent=10.0 +reverseCurrent=-10.0 +reverseTime=0.4 unmountCurrent=10.0 diff --git a/script/client/PShellClient.py b/script/client/PShellClient.py new file mode 100644 index 0000000..a40b1df --- /dev/null +++ b/script/client/PShellClient.py @@ -0,0 +1,369 @@ +import threading +import time +import sys +import requests +import json + +try: + from urllib import quote # Python 2 +except ImportError: + from urllib.parse import quote # Python 3 + +try: + from sseclient import SSEClient +except: + SSEClient = None + + +class PShellClient: + def __init__(self, url): + self.url = url + self.sse_event_loop_thread = None + self.subscribed_events = None + self.event_callback = None + + def _get_response(self, response, is_json=True): + if response.status_code != 200: + raise Exception(response.text) + return json.loads(response.text) if is_json else response.text + + def _get_binary_response(self, response): + if response.status_code != 200: + raise Exception(response.text) + return response.raw.read() + + def get_version(self): + """Return application version. + + Args: + + Returns: + String with application version. + + """ + return self._get_response(requests.get(url=self.url+"/version"), False) + + def get_config(self): + """Return application configuration. + + Args: + + Returns: + Dictionary. + """ + return self._get_response(requests.get(url=self.url+"/config")) + + def get_state(self): + """Return application state. + + Args: + + Returns: + String: Invalid, Initializing,Ready, Paused, Busy, Disabled, Closing, Fault, Offline + """ + return self._get_response(requests.get(url=self.url+"/state")) + + def get_logs(self): + """Return application logs. + + Args: + + Returns: + List of logs. + Format of each log: [date, time, origin, level, description] + + """ + return self._get_response(requests.get(url=self.url+"/logs")) + + def get_history(self, index): + """Access console command history. + + Args: + index(int): Index of history entry (0 is the most recent) + + Returns: + History entry + + """ + return self._get_response(requests.get(url=self.url+"/history/"+str(index)), False) + + def get_script(self, path): + """Return script. + + Args: + path(str): Script path (absolute or relative to script folder) + + Returns: + String with file contents. + + """ + return self._get_response(requests.get(url=self.url+"/script/"+str(path)), False) + + def get_devices(self): + """Return global devices. + + Args: + + Returns: + List of devices. + Format of each device record: [name, type, state, value, age] + + """ + return self._get_response(requests.get(url=self.url+"/devices")) + + def abort(self, command_id=None): + """Abort execution of command + + Args: + command_id(optional, int): id of the command to be aborted. + if None (default), aborts the foreground execution. + + Returns: + + """ + if command_id is None: + requests.get(url=self.url+"/abort") + else: + return requests.get(url=self.url+"/abort/"+str(command_id)) + + def reinit(self): + """Reinitialize the software. + + Args: + + Returns: + + """ + requests.get(url=self.url+"/reinit") + + def stop(self): + """Stop all devices implementing the 'Stoppable' interface. + + Args: + + Returns: + + """ + requests.get(url=self.url+"/stop") + + def update(self): + """Update all global devices. + + Args: + + Returns: + + """ + requests.get(url=self.url+"/update") + + def eval(self,statement): + """Evaluates a statement in the interpreter. + If the statement finishes by '&', it is executed in background. + Otherwise statement is executed in foreground (exclusive). + + Args: + statement(str): input statement + + Returns: + String containing the console return. + If an exception is produces in the interpretor, it is re-thrown here. + """ + statement = quote(statement) + return self._get_response(requests.get(url=self.url+"/eval/"+statement), False) + + def run(self,script, pars=None, background=False): + """Executes script in the interpreter. + + Args: + script(str): name of the script (absolute or relative to the script base folder). Extension may be omitted. + pars(optional, list or dict): if a list is given, it sets sys.argv for the script. + If a dict is given, it sets global variable for the script. + background(optional, bool): if True script is executed in background. + + Returns: + Return value of the script. + If an exception is produces in the interpretor, it is re-thrown here. + """ + return self._get_response(requests.put(url=self.url+"/run", json={"script":script, "pars":pars, "background":background, "async":False })) + + def start_eval(self,statement): + """Starts evaluation of a statement in the interpreter. + If the statement finishes by '&', it is executed in background. + Otherwise statement is executed in foreground (exclusive). + + Args: + statement(str): input statement + + Returns: + Command id (int), which is used to retrieve command execution status/result (get_result). + """ + statement = quote(statement) + return int(self._get_response(requests.get(url=self.url+"/evalAsync/"+statement), False)) + + def start_run(self,script, pars=None, background=False): + """Starts execution of a script in the interpreter. + + Args: + script(str): name of the script (absolute or relative to the script base folder). Extension may be omitted. + pars(optional, list or dict): if a list is given, it sets sys.argv for the script. + If a dict is given, it sets global variable for the script. + background(optional, bool): if True script is executed in background. + + Returns: + Command id (int), which is used to retrieve command execution status/result (get_result). + """ + return int(self._get_response(requests.put(url=self.url+"/run", json={"script":script, "pars":pars, "background":background, "async":True }))) + + def get_result(self, command_id=-1): + """Gets status/result of a command executed asynchronously (start_eval and start_run). + + Args: + command_id(optional, int): command id. If equals to -1 (default) return status/result of the foreground task. + + Returns: + Dictionary with the fields: 'id' (int): command id + 'status' (str): unlaunched, invalid, removed, running, aborted, failed or completed. + 'exception' (str): if status equals 'failed', holds exception string. + 'return' (obj): if status equals 'completed', holds return value of script (start_run) + or console return (start_eval) + """ + return self._get_response(requests.get(url=self.url+"/result/"+str(command_id))) + + def help(self, input = ""): + """Returns help or auto-completion strings. + + Args: + input(optional, str): - ":" for control commands + - "" for builtin functions + - "devices" for device names + - builtin function name for function help + - else contains entry for auto-completion + + Returns: + List + + """ + return self._get_response(requests.get(url=self.url+"/autocompletion/" + input)) + + def get_contents(self, path=None): + """Returns contents of data path. + + Args: + path(optional, str): Path to data relative to data home path. + - Folder + - File + - File (data root) | internal path + - internal path (on currently open data root) + + Returns: + List of contents + + """ + return self._get_response(requests.get(url=self.url+ "/contents" + ("" if path is None else ( "/"+path))), False) + + def get_data(self, path, type="txt"): + """Returns data on a given path. + + Args: + path(str): Path to data relative to data home path. + - File (data root) | internal path + - internal path (on currently open data root) + type(optional, str): txt, "json", "bin", "bs" + + Returns: + Data accordind to selected format/. + + """ + if type == "json": + return self._get_response(requests.get(url=self.url+ "/data-json/"+path), True) + elif type == "bin": + return self._get_binary_response(requests.get(url=self.url+"/data-bin/"+path, stream=True)) + elif type == "bs": + from collections import OrderedDict + bs = self._get_binary_response(requests.get(url=self.url+"/data-bs/"+path, stream=True)) + index=0 + msg = [] + for i in range(4): + size =int.from_bytes(bs[index:index+4], byteorder='big', signed=False) + index=index+4 + msg.append(bs[index:index+size]) + index=index+size + [main_header, data_header, data, timestamp] = msg + main_header = json.loads(main_header, object_pairs_hook=OrderedDict) + data_header = json.loads(data_header, object_pairs_hook=OrderedDict) + channel = data_header["channels"][0] + channel["encoding"] = "<" if channel.get("encoding", "little") else ">" + from bsread.data.helpers import get_channel_reader + channel_value_reader = get_channel_reader(channel) + return channel_value_reader(data) + + return self._get_response(requests.get(url=self.url+ "/data" + ("" if path is None else ( "/"+path))), False) + + def print_logs(self): + for l in self.get_logs(): + print ("%s %s %-20s %-8s %s" % tuple(l)) + + def print_devices(self): + for l in self.get_devices(): + print ("%-16s %-32s %-10s %-32s %s" % tuple(l)) + + def print_help(self, input = ""): + for l in self.help(input): + print (l) + + #Events + def _sse_event_loop_task(self): + try: + while True: + try: + messages = SSEClient(self.url+"/events") + for msg in messages: + if (self.subscribed_events is None) or (msg.event in self.subscribed_events): + try: + value = json.loads(msg.data) + except: + value = str(msg.data) + self.event_callback(msg.event, value) + except IOError as e: + #print(e) + pass + except: + print("Error:", sys.exc_info()[1]) + #raise + finally: + print ("Exit SSE loop task") + self.sse_event_loop_thread = None + + + def start_sse_event_loop_task(self, subscribed_events = None, event_callback = None): + """ + Initializes server event loop task. + Args: + subscribed_events: list of event names to substribe to. If None subscribes to all. + event_callback: callback function. If None, self.on_event is called instead. + + Usage example: + def on_event(name, value): + if name == "state": + print ("State changed: ", value) + elif name == "record": + print ("Received scan record: ", value) + + pc.start_sse_event_loop_task(["state", "record"], on_event) + + """ + self.event_callback = event_callback if event_callback is not None else self.on_event + self.subscribed_events = subscribed_events + if SSEClient is not None: + if self.sse_event_loop_thread is None: + self.sse_event_loop_thread = threading.Thread(target=self._sse_event_loop_task, \ + args = (), \ + kwargs={}, \ + daemon=True) + self.sse_event_loop_thread.start() + else: + raise Exception ("sseclient library is not instlled: server events are not available") + + def on_event(self, name, value): + pass + \ No newline at end of file diff --git a/script/client/TellClient.py b/script/client/TellClient.py new file mode 100644 index 0000000..c4eac66 --- /dev/null +++ b/script/client/TellClient.py @@ -0,0 +1,167 @@ +from PShellClient import PShellClient +import json +import time +import sys + + + +class TellClient(PShellClient): + def __init__(self, url): + PShellClient.__init__(self, url) + self.start_sse_event_loop_task(["state", "shell"]) + self.state = self.get_state() + self.debug=False + + def on_event(self, name, value): + if name == "state": + self.state = value + print ("State: ", value) + elif name == "shell": + if self.debug: + print ("> ", value) + + def get_state(self): + self.state = PShellClient.get_state(self) + return self.state + + def wait_ready(self): + count = 0 + #Monitors event but polls every second just n case an event is missed + while (True): + if self.state != "Busy": + break + time.sleep(0.01) + count = count + 1 + if count>=100: + count=0 + self.get_state() + if self.state != "Ready": + raise Exception("Invalid state: " + str(self.state)) + + + def get_samples_info(self): + return json.loads(self.eval("get_samples_info()&")) + + def set_samples_info(self, info): + #c.run("data/set_samples_info", pars= [info,], background=True) + self.eval("set_samples_info(" + json.dumps(info) + ")&") + + def start_cmd(self, cmd, *argv): + cmd = cmd + "(" + for a in argv: + cmd = cmd + (("'" + a + "'") if type(a) is str else str(a) ) + ", " + cmd = cmd + ")" + ret = self.start_eval(cmd) + self.get_state() + return ret + + def wait_cmd(self, cmd): + self.wait_ready() + result = self.get_result(cmd) + #print (result) + if result["exception"] is not None: + raise Exception(result["exception"] ) + return result["return"] + + def mount(self, segment, puck, sample, force=False, read_dm=False): + #return self.run("motion/mount", pars= [segment,puck, sample, force, read_dm], background=True) + return self.start_cmd("mount", segment, puck, sample, force, read_dm) + + def unmount(self, segment = None, puck = None, sample = None, force=False): + return self.start_cmd("unmount", segment, puck, sample, force) + + def scan_pin(self, segment, puck, sample, force=False): + return self.start_cmd("scan_pin", segment, puck, sample, force) + + def scan_puck(self, segment, puck, force=False): + return self.start_cmd("scan_puck", segment, puck, force) + + def dry(heat_time=30.0, speed=0.5, wait_cold = 30.0): + return self.start_cmd("dry", heat_time, speed, wait_cold) + + def move_cold(self): + return self.start_cmd("move_cold") + + def trash(self): + return self.start_cmd("trash") + + def abort_cmd(self): + self.abort() + self.eval("robot.stop_task()&") + + def get_mounted_sample(self): + ret = self.eval("get_setting('mounted_sample_position')&").strip() + return None if len(ret)==0 else ret + + def get_system_check(self): + try: + ret = self.eval("system_check()&") + except Exception as ex: + return ex + return "Ok" + + def get_robot_state(self): + return self.eval("robot.state&") + + + def get_robot_status(self): + status = self.eval("robot.take()&") + return status + + def get_detected_pucks(self): + return self.eval("get_detected_pucks()&") + + + def print_info(self): + print ("State: " + str(self.get_state())) + print ("Mounted Sample: " + str(self.get_mounted_sample())) + print ("System Check: " + str(self.get_system_check())) + print ("Robot state: " + str(self.get_robot_state())) + print ("Robot status: " + str(self.get_robot_status())) + print ("Detected Pucks: " + str(self.get_detected_pucks())) + print ("") + +if __name__ == "__main__": + tell = TellClient("http://Alexandres-MBP.psi.ch:8080") + tell.print_info() + + info = [ + { + "userName": "User", + "dewarName": "Dewar", + "puckName": "Puck", + "puckBarcode": "XXX0001", + "puckType": "Minispine", + "puckAddress": "", + "sampleName": "Sample", + "sampleBarcode": "YYY0001", + "samplePosition": "1", + "sampleStatus": "Present", + "sampleMountCount": "0" , + }, + ] + print (tell.get_samples_info()) + tell.set_samples_info(info) + print (tell.get_samples_info()) + + tell.abort_cmd() + + cmd = tell.move_cold() + print (tell.wait_cmd(cmd)) + + cmd = tell.trash() + print (tell.wait_cmd(cmd)) + + cmd = tell.scan_pin("A", 1, 1) + print (tell.wait_cmd(cmd)) + + cmd = tell.scan_puck("A", 1, 1) + print (tell.wait_cmd(cmd)) + + cmd = tell.mount("A", 1, 1) + print (tell.wait_cmd(cmd)) + + print ("Mounted sample: " + str(tell.get_mounted_sample())) + cmd = tell.unmount() + print (tell.wait_cmd(cmd)) + print ("Mounted sample: " + str(tell.get_mounted_sample())) diff --git a/script/client/tell.py b/script/client/tell.py new file mode 100644 index 0000000..92b2569 --- /dev/null +++ b/script/client/tell.py @@ -0,0 +1,42 @@ +import sys +from TellClient import TellClient +import code + +tell = TellClient("http://Alexandres-MBP.psi.ch:8080") + +def info(): + tell.print_info() + +def help(): + print ("Commands: help(), info(), mount(segment, puck, sample), unmount(), scan(segment, puck, sample=None), move_cold(), trash(), abort()\n") + +def mount(segment, puck, sample): + tell.mount(segment, puck, sample, True, True) + +def move_cold(): + tell.mount(force=True) + +def unmount(): + tell.move_cold() + +def trash(): + tell.trash() + +def abort(): + tell.abort_cmd() + +def scan(segment, puck, sample=None): + if sample is None: + tell.scan_pin(segment, puck, True) + else: + tell.scan_pin(segment, puck, sample, True) + +info() +help() + +#for line in sys.stdin: +# tell.print_info() +#print ("", end='\r> ') + +code.interact(local=locals()) + diff --git a/script/data/samples.py b/script/data/samples.py index 55c7b9a..f6ba272 100644 --- a/script/data/samples.py +++ b/script/data/samples.py @@ -48,8 +48,8 @@ def restore_samples_info(): set_samples_info(info) def get_samples_info(as_json=True): - global sample_info - return json.dumps(samples_info) if as_json else samples_info + global samples_info + return json.dumps(to_list(samples_info)) if as_json else samples_info def has_puck_datamatrix(datamatrix): if samples_info is not None: diff --git a/script/devices/Hexiposi.py b/script/devices/Hexiposi.py index c168204..52c86c4 100644 --- a/script/devices/Hexiposi.py +++ b/script/devices/Hexiposi.py @@ -5,15 +5,19 @@ import json class Hexiposi(DiscretePositionerBase): def __init__(self, name, url): DiscretePositionerBase.__init__(self, name, ["A","B","C","D","E","F"]) + self.PORT_SET=8002 + self.PORT_GET=8002 if not url.startswith("http://"): url = "http://" + url - if not url.endswith("/"): - url = url + "/" - self.url = url + if not url.endswith(":"): + url = url + ":" + self.url_set = url + str (self.PORT_SET)+ "/hexiposi/" + self.url_get = url + str (self.PORT_GET)+ "/hexiposi/" self.moved = True self.homing_state = State.Disabled self.rback = self.UNKNOWN_POSITION self.timeout = 3.0 + self.offline = False def doInitialize(self): super(Hexiposi, self).doInitialize() @@ -25,40 +29,46 @@ class Hexiposi(DiscretePositionerBase): return json.loads(response.text) def get_status(self): - self.status = self.get_response(requests.get(url=self.url+"get", timeout=self.timeout)) - self.estop = self.status["estop"] - self.homed = self.status["homed"] - self.error = self.status["errorCode"] - self.remote = self.status["mode"] == "remote" - self.moving = self.status["errorCode"] - self.pos = self.status["position"] - self.moving = self.status["moving"] - self.offset = self.status["offset"] - self.dpos = self.status["discretePosition"] - if (self.homed==False): rback = self.UNKNOWN_POSITION - elif self.dpos == 1: rback = "A" - elif self.dpos == 2: rback = "B" - elif self.dpos == 4: rback = "C" - elif self.dpos == 8: rback = "D" - elif self.dpos == 16: rback = "E" - elif self.dpos == 32: rback = "F" - else: rback = self.UNKNOWN_POSITION - if (rback == self.UNKNOWN_POSITION) or (rback != self.rback): - self.moved = True - self.rback = rback - return self.status - + try: + self.status = self.get_response(requests.get(url=self.url_get+"get", timeout=self.timeout)) + self.estop = self.status["estop"] + self.homed = self.status["homed"] + self.error = self.status["errorCode"] + self.remote = self.status["mode"] == "remote" + self.moving = self.status["errorCode"] + self.pos = self.status["position"] + self.moving = self.status["moving"] + self.offset = self.status["offset"] + self.dpos = self.status["discretePosition"] + if (self.homed==False): rback = self.UNKNOWN_POSITION + elif self.dpos == 1: rback = "A" + elif self.dpos == 2: rback = "B" + elif self.dpos == 4: rback = "C" + elif self.dpos == 8: rback = "D" + elif self.dpos == 16: rback = "E" + elif self.dpos == 32: rback = "F" + else: rback = self.UNKNOWN_POSITION + if (rback == self.UNKNOWN_POSITION) or (rback != self.rback): + self.moved = True + self.rback = rback + self.offline = False + return self.status + except: + self.offline = True + self.updateState() + raise + def set_deadband(self, value = 0.1): #degrees - ret = self.get_response(requests.get(url=self.url+"setDeadband?deadband=" + str(value), timeout=self.timeout)) + ret = self.get_response(requests.get(url=self.url_set+"setDeadband?deadband=" + str(value), timeout=self.timeout)) if ret["deadbandOutput"] == value: return value raise Excepiton("Error setting deadband: " + str(ret)) def move_pos(self, pos): - return self.get_response(requests.get(url=self.url+"set?pos=" + str(pos), timeout=self.timeout)) + return self.get_response(requests.get(url=self.url_set+"set?pos=" + str(pos), timeout=self.timeout)) def move_home(self): - ret = self.get_response(requests.get(url=self.url+"set?home=1", timeout=self.timeout)) + ret = self.get_response(requests.get(url=self.url_set+"set?home=1", timeout=self.timeout)) try: self.waitState(self.homing_state,1200) except: @@ -66,10 +76,10 @@ class Hexiposi(DiscretePositionerBase): return ret def stop_move(self): - return self.get_response(requests.get(url=self.url+"set?stop=1", timeout=self.timeout)) + return self.get_response(requests.get(url=self.url_set+"set?stop=1", timeout=self.timeout)) def set_offset(self, offset): - return self.get_response(requests.get(url=self.url+"setOffset?offset="+str(offset), timeout=self.timeout)) + return self.get_response(requests.get(url=self.url_set+"setOffset?offset="+str(offset), timeout=self.timeout)) def doUpdate(self): self.get_status() @@ -124,7 +134,9 @@ class Hexiposi(DiscretePositionerBase): # return self.moving == False def updateState(self): - if self.homed == False: + if self.offline: + self.setState(State.Offline) + elif self.homed == False: self.setState(self.homing_state) elif self.moving: self.setState(State.Busy) @@ -133,8 +145,8 @@ class Hexiposi(DiscretePositionerBase): -#http://myriotell:8002/hexiposi/get -dev = Hexiposi("hexiposi", "myriotell:8002/hexiposi") +#http://myriotell:8003/hexiposi/get +dev = Hexiposi("hexiposi", "myriotell") add_device(dev, True) hexiposi.polling=1000 diff --git a/script/devices/RobotSC.py b/script/devices/RobotSC.py index af57b8b..f55c281 100644 --- a/script/devices/RobotSC.py +++ b/script/devices/RobotSC.py @@ -28,7 +28,8 @@ class RobotSC(RobotTCP): RobotTCP.__init__(self, name, server, timeout, retries) self.set_tasks(["getDewar", "putDewar", "putGonio", "getGonio", "recover", "moveDewar", "moveCold", "movePark", "moveGonio","moveHeater", "moveScanner","moveHome", "moveAux"]) self.set_known_points(["pPark", "pGonio", "pDewar", "pGonioG", "pScan", "pHeater", "pHeat", "pHeatB", "pScanStop","pHelium", "pHome", "pCold", "pAux"]) - self.setPolling(DEFAULT_ROBOT_POLLING) + self.setPolling(DEFAULT_ROBOT_POLLING) + self.setSimulated() def move_dewar(self): self.start_task('moveDewar') diff --git a/script/devices/RobotTCP.py b/script/devices/RobotTCP.py index 10fb0f1..e2a0454 100644 --- a/script/devices/RobotTCP.py +++ b/script/devices/RobotTCP.py @@ -141,6 +141,8 @@ class RobotTCP(TcpDevice, Stoppable): raise Exception("Exceeded maximum message size") self.getLogger().finer("TX = '" + str(tx)+ "'") if (self.trailer != None): tx = tx + self.trailer + if self.isSimulated(): + return "" rx = self.sendReceive(tx, msg_id, self.trailer , 0, self.timeout if timeout is None else timeout, self.retries) rx=rx[:-1] #Remove 0A self.getLogger().finer("RX = '" + str(rx) + "'") @@ -257,14 +259,20 @@ class RobotTCP(TcpDevice, Stoppable): self.set_trsf(trsf, name+".trsf") def eval_int(self, cmd): + if self.isSimulated(): + return 0 self.evaluate("tcp_n=" + cmd) return self.get_int() def eval_float(self, cmd): + if self.isSimulated(): + return 0.0 self.evaluate("tcp_n=" + cmd) return self.get_float() def eval_bool(self, cmd): + if self.isSimulated(): + return False self.evaluate("tcp_b=" + cmd) return self.get_bool() @@ -547,6 +555,8 @@ class RobotTCP(TcpDevice, Stoppable): self.execute('kill', str(name), timeout=5000) def get_task_status(self, name): + if self.isSimulated(): + return (-1,"Stopped") code = self.eval_int('taskStatus("' + str(name)+ '")') #TODO: String assignments in $exec causes application to freeze #status = self @@ -575,7 +585,6 @@ class RobotTCP(TcpDevice, Stoppable): self.check_task() if self.current_task is not None: print "Ongoing task: " + self.current_task - if (not self.settled) or (self.current_task is not None): self.setState(State.Busy) elif not self.empty: self.setState(State.Paused) else: self.setState(State.Ready) @@ -584,12 +593,19 @@ class RobotTCP(TcpDevice, Stoppable): try: start = time.time() cur_task = self.current_task #Can change in background so cache it - sts = self.execute("get_status", cur_task) + if self.isSimulated(): + sts = [1, 1,"1", 100, "1", "1", 0, 0, \ + 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, \ + ] + else: + sts = self.execute("get_status", cur_task) self._update_working_mode(int(sts[0]), int(sts[1])) self.powered = sts[2] == "1" self.speed = int(sts[3]) self.empty = sts[4] == "1" self.settled = sts[5] == "1" + #TODO: add tool open if cur_task is not None: if int(sts[6]) < 0: @@ -602,14 +618,14 @@ class RobotTCP(TcpDevice, Stoppable): self.joint_pos[i] = float(sts[8 + i]) for i in range(6): self.cartesian_pos[i] = float(sts[14 + i]) - + ev_index = 20 #7 ev = sts[ev_index] if len(sts)>ev_index else "" if len(ev.strip()) >0: self.getLogger().info(ev) self.on_event(ev) - - self._update_state() + + self._update_state() self.reset = False self.setCache({"powered": self.powered, "speed": self.speed, @@ -621,6 +637,7 @@ class RobotTCP(TcpDevice, Stoppable): "open": self.tool_open, "pos": self.get_current_point_cached() if self.state==State.Ready else None #TODO: make it calculated in robot by polling funtion }, None) + if self.cartesian_motors_enabled: for m in self.cartesian_motors: m.readback.update() diff --git a/script/devices/SmartMagnet.py b/script/devices/SmartMagnet.py index c0ad89a..29e70b7 100644 --- a/script/devices/SmartMagnet.py +++ b/script/devices/SmartMagnet.py @@ -6,6 +6,8 @@ class SmartMagnet(DeviceBase): "restingCurrent":0.0, "mountCurrent":0.0, "unmountCurrent":0.0, + "reverseCurrent":0.0, + "reverseTime":0.0, })) def doInitialize(self): @@ -89,8 +91,8 @@ class SmartMagnet(DeviceBase): def set_unmount_current(self): self.set_current(self.config.getFieldValue("unmountCurrent")) - def set_remanence_current(self): - self.set_current(self.config.getFieldValue("remanenceCurrent")) + def set_reverse_current(self): + self.set_current(self.config.getFieldValue("reverseCurrent")) def set_default_current(self): if self.is_mounted(): @@ -104,6 +106,10 @@ class SmartMagnet(DeviceBase): #Setting resting current to better detect sample def enforce_sample_detection(self): + reverse_wait = float(self.config.getFieldValue("reverseTime")) + if reverse_wait >0: + self.set_reverse_current() + time.sleep(reverse_wait) if not self.is_resting_current(): self.set_resting_current() time.sleep(0.2) diff --git a/script/local.py b/script/local.py index dd0d54d..ad48749 100644 --- a/script/local.py +++ b/script/local.py @@ -25,10 +25,10 @@ BARCODE_READER_SCAN_PUCKS = "barcode_reader_scan_pucks" def is_imaging_enabled(): setting = get_setting(IMAGING_ENABLED_PREFERENCE) - return not (str(setting) == 'False') + return not (str(setting).lower() == 'false') def set_imaging_enabled(value): - set_setting(IMAGING_ENABLED_PREFERENCE, True if value else False ) + set_setting(IMAGING_ENABLED_PREFERENCE, (True if value else False) ) def assert_imaging_enabled(): if is_imaging_enabled() == False: @@ -69,8 +69,9 @@ for script in ["devices/RobotSC", "devices/Wago", "devices/BarcodeReader", "devi except: print >> sys.stderr, traceback.format_exc() -add_device(img.getContrast(), force = True) -add_device(img.getCamera(), force = True) +if is_imaging_enabled(): + add_device(img.getContrast(), force = True) + add_device(img.getCamera(), force = True) ################################################################################################### @@ -103,8 +104,9 @@ run("motion/calibrate_tool") run("motion/scan_pin") run("motion/robot_recover") run("motion/recover") -run("imgproc/Utils") run("tools/Math") +if is_imaging_enabled(): + run("imgproc/Utils") def system_check(robot_move=True): if not air_pressure_ok.read(): @@ -176,7 +178,8 @@ try: except: print >> sys.stderr, traceback.format_exc() -try: +if is_imaging_enabled(): + try: import ch.psi.pshell.device.Camera as Camera #img.camera.setColorMode(Camera.ColorMode.Mono) #img.camera.setDataType(Camera.DataType.UInt8) @@ -196,7 +199,7 @@ try: img.camera.stop() img.camera.start() -except: + except: print >> sys.stderr, traceback.format_exc() diff --git a/script/motion/calibrate_tool.py b/script/motion/calibrate_tool.py index 5303b87..5357ef0 100644 --- a/script/motion/calibrate_tool.py +++ b/script/motion/calibrate_tool.py @@ -29,4 +29,4 @@ def calibrate_tool(): robot.put_calibration_tool() - #robot.save_program() + robot.save_program() diff --git a/script/motion/dry.py b/script/motion/dry.py index d3d97d3..18d5d8e 100644 --- a/script/motion/dry.py +++ b/script/motion/dry.py @@ -8,7 +8,12 @@ def dry(heat_time=30.0, speed=0.5, wait_cold = 30.0): Else move to cold and wait (in seconds) before returning. """ print "dry" - + + + if robot.simulated: + time.sleep(3.0) + return + #Initial chec robot.assert_no_task() robot.reset_motion() diff --git a/script/motion/mount.py b/script/motion/mount.py index f3a712d..c402ad2 100644 --- a/script/motion/mount.py +++ b/script/motion/mount.py @@ -22,6 +22,15 @@ def mount(segment, puck, sample, force=False, read_dm=False): #Initial checks assert_valid_address(segment, puck, sample) assert_puck_detected(segment, puck) + + if robot.simulated: + time.sleep(3.0) + mount_sample_detected = True + mount_sample_id = "YYY0001" + update_samples_info_sample_mount(get_puck_name(segment, puck), sample, mount_sample_detected, mount_sample_id) + set_setting("mounted_sample_position", get_sample_name(segment, puck, sample)) + return [mount_sample_detected, mount_sample_id] + robot.assert_no_task() robot.reset_motion() robot.wait_ready() @@ -84,8 +93,7 @@ def mount(segment, puck, sample, force=False, read_dm=False): except: dry_mount_count = 0 set_setting("dry_mount_counter", dry_mount_count+1) - #TODO: Auto-dry procedure - + if is_aux: robot.move_home() else: @@ -98,6 +106,7 @@ def mount(segment, puck, sample, force=False, read_dm=False): raise Exception("No pin detected on gonio") if is_force_dry(): + smart_magnet.set_default_current() print "Auto dry" log("Starting auto dry", False) dry() diff --git a/script/motion/move_cold.py b/script/motion/move_cold.py index 52dc9c9..89c8920 100644 --- a/script/motion/move_cold.py +++ b/script/motion/move_cold.py @@ -3,6 +3,10 @@ def move_cold(): """ print "move_cold" + if robot.simulated: + time.sleep(3.0) + return + #Initial checks robot.assert_no_task() robot.reset_motion() diff --git a/script/motion/put_gonio.py b/script/motion/put_gonio.py index a29e1db..c23463f 100644 --- a/script/motion/put_gonio.py +++ b/script/motion/put_gonio.py @@ -2,6 +2,11 @@ def put_gonio(force=False): """ """ print "put_gonio: ", force + + + if robot.simulated: + time.sleep(3.0) + return #Initial checks robot.assert_no_task() diff --git a/script/motion/scan_pin.py b/script/motion/scan_pin.py index 032f0ca..7683694 100644 --- a/script/motion/scan_pin.py +++ b/script/motion/scan_pin.py @@ -6,6 +6,10 @@ def scan_pin(segment, puck, sample, force=False): assert_valid_address(segment, puck, sample) assert_puck_detected(segment, puck) is_aux = (segment == AUX_SEGMENT) + + if robot.simulated: + time.sleep(0.5) + return "Present" robot.assert_no_task() robot.reset_motion() diff --git a/script/motion/trash.py b/script/motion/trash.py index 96eee28..0057a2d 100644 --- a/script/motion/trash.py +++ b/script/motion/trash.py @@ -2,6 +2,10 @@ def trash(): """ """ print "trash" + + if robot.simulated: + time.sleep(3.0) + return #Initial checks robot.assert_no_task() diff --git a/script/motion/unmount.py b/script/motion/unmount.py index 84b2464..9da17d1 100644 --- a/script/motion/unmount.py +++ b/script/motion/unmount.py @@ -14,6 +14,12 @@ def unmount(segment = None, puck = None, sample = None, force=False): #Initial checks assert_valid_address(segment, puck, sample) assert_puck_detected(segment, puck) + + if robot.simulated: + time.sleep(3.0) + update_samples_info_sample_unmount(get_puck_name(segment, puck), sample) + set_setting("mounted_sample_position", None) + return robot.assert_no_task() robot.reset_motion() diff --git a/script/test/TestCalib.py b/script/test/TestCalib.py new file mode 100644 index 0000000..2cc311b --- /dev/null +++ b/script/test/TestCalib.py @@ -0,0 +1,30 @@ + +print "calibrate_tool" + +#Initial checks +robot.assert_no_task() +robot.reset_motion() +robot.wait_ready() +robot.assert_cleared() +#robot.assert_in_known_point() + +#Enabling +enable_motion() + +(detected, dm) = move_scanner() + +if detected: + print "Pin detected, trashing..." + trash() + (detected, dm) = move_scanner() + if detected: + raise Exception("Cannot trash pin") + +robot.open_tool() +robot.get_calibration_tool() + +run("calibration/ToolCalibration3") + +robot.put_calibration_tool() + +robot.save_program()