Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions action_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]):
Expand Down
48 changes: 37 additions & 11 deletions exec_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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:
Expand All @@ -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':
Expand All @@ -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)
Expand Down
41 changes: 25 additions & 16 deletions gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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')
Expand Down Expand Up @@ -85,19 +86,25 @@ 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:
raise RuntimeError("connection failed.")
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):
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -211,22 +217,23 @@ 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)
with self.stop_flag.get_lock():
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
Expand All @@ -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()
173 changes: 173 additions & 0 deletions rotationtable_controller.py
Original file line number Diff line number Diff line change
@@ -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)