diff --git a/action_parser.py b/action_parser.py index 3f9bd2e..facf4a3 100644 --- a/action_parser.py +++ b/action_parser.py @@ -72,6 +72,8 @@ def action_parser(raw_action: str): (action[1] == 'x' or action[1] == 'y' or action[1] == 'z') and \ isfloat(action[2]): # NOTE: add velocity? action_list.append(action) + elif action[0] == 'rot' and len(action) == 2 and action[1].isdigit(): + action_list.append(action) elif action[0] == 'shutter' and len(action) == 1: action_list.append(action) elif action[0] == 'sleep' and len(action) == 2 and isfloat(action[1]): diff --git a/exec_actions.py b/exec_actions.py index 75cb7b5..7064ce4 100644 --- a/exec_actions.py +++ b/exec_actions.py @@ -2,12 +2,23 @@ import time import signal import subprocess +from typing import Union from StackShot3X_API_for_Python.commdefs import * from action_parser import action_parser from StackShot3X_API_for_Python.stackshot_controller import StackShotController - -def exec_actions(stop_flag, raw_actions: str, controller: StackShotController, brackets: int, doFocusStacking: bool, doMetashape: bool, moveSpeedPercent: int, config): +from rotationtable_controller import RotationTableController + +def exec_actions( + stop_flag, + raw_actions: str, + stackshot_controller: StackShotController, + rotationtable_controller: RotationTableController, + brackets: int, + doFocusStacking: bool, + doMetashape: bool, + moveSpeedPercent: int, + config): # validation and parse actions try: action_queue = action_parser(raw_actions) @@ -31,9 +42,9 @@ def exec_actions(stop_flag, raw_actions: str, controller: StackShotController, b try: # send stop command in advance to prevent infinite move, - controller.stop(RailAxis.X) - controller.stop(RailAxis.Y) - controller.stop(RailAxis.Z) + stackshot_controller.stop(RailAxis.X) + stackshot_controller.stop(RailAxis.Y) + stackshot_controller.stop(RailAxis.Z) # execute all actions for action in action_queue: @@ -42,7 +53,7 @@ def exec_actions(stop_flag, raw_actions: str, controller: StackShotController, b if stop_flag.value == 1: # true return - # move axis + # move axis (StackShot) if action[0] == 'move': axis = None if action[1] == 'x': @@ -52,21 +63,36 @@ def exec_actions(stop_flag, raw_actions: str, controller: StackShotController, b elif action[1] == 'z': axis = RailAxis.Z - controller.move_at_speed(axis, RailDir.FWD, float(action[2]), moveSpeedPercent/100.0) + stackshot_controller.move_at_speed(axis, RailDir.FWD, float(action[2]), moveSpeedPercent/100.0) # wait for rail stop - while controller.get_status(axis) != RailStatus.IDLE: + while stackshot_controller.get_status(axis) != RailStatus.IDLE: time.sleep(0.2) + # rotate (Rotation Table) + elif action[0] == 'rot': + ret = rotationtable_controller.set_speed(500) # TODO とりあえずハードコード。UI追加する? + if ret == False: + print("Error in rotation table.") + while rotationtable_controller.get_status(): + time.sleep(0.1) + + angle = int(action[1]) + ret = rotationtable_controller.rotate(angle) + if ret == False: + print("Error in rotation table.") + while rotationtable_controller.get_status(): + time.sleep(0.1) + elif action[0] == 'sleep': time.sleep(float(action[1])) - # shoot camera + # shoot camera (StackShot) elif action[0] == 'shutter': - controller.shutter(1, 1., 2.) # NOTE + stackshot_controller.shutter(1, 1., 2.) # NOTE # wait for finish shutter - while controller.get_status(RailAxis.ANY) != RailStatus.IDLE: + while stackshot_controller.get_status(RailAxis.ANY) != RailStatus.IDLE: time.sleep(0.2) while len(os.listdir(image_src_folder)) < brackets: time.sleep(0.2) diff --git a/gui.py b/gui.py index bcbed93..bccd063 100644 --- a/gui.py +++ b/gui.py @@ -21,6 +21,7 @@ from StackShot3X_API_for_Python.stackshot_controller import StackShotController from action_parser import action_parser from exec_actions import exec_actions +from rotationtable_controller import RotationTableController def isValidValue(s, min, max): @@ -36,16 +37,16 @@ def isValidValue(s, min, max): return False -def moveAxis(controller, axis, dir, dist, speedPercent): +def moveAxis(stackshot_controller, axis, dir, dist, speedPercent): try: - controller.move_at_speed(axis, dir, dist, speedPercent / 100.0) + stackshot_controller.move_at_speed(axis, dir, dist, speedPercent / 100.0) except: # pass print('move') -def stopAxis(controller, axis): +def stopAxis(stackshot_controller, axis): try: - controller.stop(axis) + stackshot_controller.stop(axis) except: # pass print('stop') @@ -85,12 +86,12 @@ def __init__(self): self.working_thread = None self.stop_flag = Value('i', 0) # 0: false, 1: true - # when start app, connect with StackShot3X - self.controller = StackShotController() + # connect with StackShot3X + self.stackshot_controller = StackShotController() try: print('connecting to Stackshot3X...') - self.controller.open() - self.working_thread = Thread(target=self.controller.get_status, args=(RailAxis.ANY,), daemon=True) + self.stackshot_controller.open() + self.working_thread = Thread(target=self.stackshot_controller.get_status, args=(RailAxis.ANY,), daemon=True) self.working_thread.start() self.working_thread.join(timeout=3) if self.working_thread.is_alive() == True: @@ -98,6 +99,12 @@ def __init__(self): except Exception as excpt: print(excpt) exit() + return + + # connect with RotationTable + self.rotationtable_controller = RotationTableController() + print('connecting to RotationTable...') + self.rotationtable_controller.start() # load config from config.ini def loadConfig(self): @@ -147,7 +154,6 @@ def loadConfig(self): self.config.write(f) f.close() - # select "Image Src Folder" PATH from file dialog and save to config def updateImageSrcFolder(self): file = QtWidgets.QFileDialog.getExistingDirectory() @@ -211,14 +217,15 @@ def moveStackShot(self, dir: RailDir, dist: float): elif self.gui.zRadioButton.isChecked() == True: axis = RailAxis.Z - moveAxis(self.controller, axis, dir, dist, self.gui.speedPercent.value()) + moveAxis(self.stackshot_controller, axis, dir, dist, self.gui.speedPercent.value()) # wait for rail stop - while self.controller.get_status(axis) != RailStatus.IDLE: + while self.stackshot_controller.get_status(axis) != RailStatus.IDLE: time.sleep(0.2) # start action def startAction(self): print('start') + self.saveBracketsAndSpeed() # set stop_flag to 0(false) @@ -226,7 +233,7 @@ def startAction(self): self.stop_flag.value = 0 # false # create and thread to execute actions - self.working_thread = Thread(target=exec_actions, args=(self.stop_flag, self.gui.actionsPanel.toPlainText(), self.controller, self.gui.brackets.value(), self.gui.doFocusStacking.isChecked(), self.gui.doMetashape.isChecked(), self.gui.speedPercent.value(), self.config), daemon=True) + self.working_thread = Thread(target=exec_actions, args=(self.stop_flag, self.gui.actionsPanel.toPlainText(), self.stackshot_controller, self.rotationtable_controller, self.gui.brackets.value(), self.gui.doFocusStacking.isChecked(), self.gui.doMetashape.isChecked(), self.gui.speedPercent.value(), self.config), daemon=True) self.working_thread.start() # stop in progress action @@ -249,10 +256,12 @@ def closeEvent(self, event: QtGui.QCloseEvent): # after stop all axis, disconnect from StackShot3X try: print('discoonnecting from Stackshot3X...') - self.controller.stop(RailAxis.X) - self.controller.stop(RailAxis.Y) - self.controller.stop(RailAxis.Z) - self.controller.close() + self.stackshot_controller.stop(RailAxis.X) + self.stackshot_controller.stop(RailAxis.Y) + self.stackshot_controller.stop(RailAxis.Z) + self.stackshot_controller.close() except Exception as excpt: print(excpt) return + + self.rotationtable_controller.end() diff --git a/rotationtable_controller.py b/rotationtable_controller.py new file mode 100644 index 0000000..95eaeaa --- /dev/null +++ b/rotationtable_controller.py @@ -0,0 +1,173 @@ +from serial import Serial +from serial.tools import list_ports +from threading import Thread +from time import sleep + + +class RotationTableController(): + def __init__(self): + self.port='' + self.baud = 115200 + self.ser = None + self.isConnect = False + self.isStart=False + + def start(self): + if self.isStart:return + self.isStart=True + Thread(target=self.connectin_loop,daemon=True).start() + + def end(self): + if not self.isStart:return + + self.isStart=False + + if self.isConnect: + self.ser.close() + self.ser=None + self.port='' + self.isConnect=False + + #シリアルポートにコマンドを送って反応を見る + def chk_connection(self,port:str): + try: + ser = Serial(port,self.baud,timeout=1.0,write_timeout=0.1,inter_byte_timeout=0.1) + cmd='ping\r\n'#死活確認コマンド + ser.write(cmd.encode()) + sleep(0.1) + res = ser.read_all() + ser.close() + if res.decode().rsplit('\r\n')[1]=='True': + return True + else: + return False + except: + return False + + #シリアルの接続監視 + #制御基板との接続を検出して自動接続する + def connectin_loop(self): + while self.isStart: + #Comポートのリスト生成 + ports = list_ports.comports() + devices = [info.device for info in ports] + #print(devices) + if self.isConnect:#切断されたポートがないかチェック + sleep(0.1) + if not self.port in devices: + print('disconnect',self.port) + self.port='' + self.isConnect=False + else: + continue + + #それぞれのポートに接続してテーブル制御ポートを見つける + for port in devices: + if self.chk_connection(port): + self.port=port + break + + #接続 + if self.port != '': + print('connect',self.port) + self.ser = Serial(self.port,self.baud,timeout=1.0) + self.isConnect=True + + + #コマンド送信 + def send_cmd(self,cmd:str): + #print(cmd) + self.ser.reset_output_buffer() + self.ser.reset_input_buffer() + self.ser.write(cmd.encode()) + sleep(0.2) + res = self.ser.read_all() + #print(res) + try: + ret = eval(res.decode().rsplit('\r\n')[1]) + except: + ret=False + + return ret + + + #回転指令 指定した角度分テーブルを回転する + #引数 回転角度[deg] 値をマイナスにすると逆回転 + #戻り値 正常:True 異常:False + def rotate(self,deg:float): + if not self.isConnect: + return False + + cmd = f'rot:{deg}\r\n' + + return self.send_cmd(cmd) + + #停止指令 テーブルの回転を即座に停止させる + #戻り値 正常:True 異常:False + def stop(self): + if not self.isConnect: + return False + + cmd = 'stop\r\n' + + return self.send_cmd(cmd) + + #速度変更指令 テーブルの回転速度を変更する(テーブル回転中は変更不可) + #引数 回転速度[Hz] 1~25000[Hz] 周波数が高いほど速度が速くなる + #戻り値 正常:True 異常:False + def set_speed(self,speed:int): + if not self.isConnect: + return False + + if 0>speed: + return False + + speed_us = int(1/speed * 1000000) + + cmd = f'set_speed:{speed_us}\r\n' + + return self.send_cmd(cmd) + + #加速度変更 テーブルの回転加速度を変更する(テーブル回転中は変更不可) + #引数 0で最大加速度、値を大きくすると加速度が小さくなり台形制御になる + #戻り値 正常:True 異常:False + def set_acc(self,acc:int): + if not self.isConnect: + return False + + cmd = f'set_acc:{acc}\r\n' + + return self.send_cmd(cmd) + + #移動ステータス テーブルの動作状態を取得する  + #戻り値 移動中:True 停止中/異常:False + def get_status(self): + if not self.isConnect: + return False + + cmd = 'get_mov\r\n' + + return self.send_cmd(cmd) + + #####以下開発用コマンドにつき通常は使用しない###### + #テーブル回転のパラメータ設定 + #引数 テーブル1回転のパルス数設定 + #戻り値 正常:True 異常:False + def set_step_per_rev(self,step:int): + if not self.isConnect: + return False + + cmd = f'set_step:{step}\r\n' + + return self.send_cmd(cmd) + + #パルス出力ピン設定(設定後に再起動必要) + #引数 pin番号(基板pin):0(6), 1(5), 2(8), 3(10), 4(9), 6(4), 7(5), 26(0), 27(1), 28(2), 29(3) + #戻り値 正常:True 異常:False + def set_pin_config(self,pls_pin:int,dir_pin:int): + if not self.isConnect: + return False + + cmd = f'set_pin:{pls_pin},{dir_pin}\r\n' + + return self.send_cmd(cmd)