Update runze pump format

This commit is contained in:
Xuwznln
2025-09-14 00:22:39 +08:00
parent c7a0ff67a9
commit 2493fb9f94
2 changed files with 80 additions and 81 deletions

View File

@@ -1,10 +1,9 @@
import asyncio
from threading import Lock, Event from threading import Lock, Event
from enum import Enum
from dataclasses import dataclass
import time import time
import traceback from dataclasses import dataclass
from typing import Any, Union, Optional, overload from enum import Enum
from threading import Lock, Event
from typing import Union, Optional
import serial.tools.list_ports import serial.tools.list_ports
from serial import Serial from serial import Serial
@@ -18,47 +17,47 @@ class RunzeSyringePumpMode(Enum):
pulse_freq_grades = { pulse_freq_grades = {
6000: "0" , 6000: "0",
5600: "1" , 5600: "1",
5000: "2" , 5000: "2",
4400: "3" , 4400: "3",
3800: "4" , 3800: "4",
3200: "5" , 3200: "5",
2600: "6" , 2600: "6",
2200: "7" , 2200: "7",
2000: "8" , 2000: "8",
1800: "9" , 1800: "9",
1600: "10", 1600: "10",
1400: "11", 1400: "11",
1200: "12", 1200: "12",
1000: "13", 1000: "13",
800 : "14", 800: "14",
600 : "15", 600: "15",
400 : "16", 400: "16",
200 : "17", 200: "17",
190 : "18", 190: "18",
180 : "19", 180: "19",
170 : "20", 170: "20",
160 : "21", 160: "21",
150 : "22", 150: "22",
140 : "23", 140: "23",
130 : "24", 130: "24",
120 : "25", 120: "25",
110 : "26", 110: "26",
100 : "27", 100: "27",
90 : "28", 90: "28",
80 : "29", 80: "29",
70 : "30", 70: "30",
60 : "31", 60: "31",
50 : "32", 50: "32",
40 : "33", 40: "33",
30 : "34", 30: "34",
20 : "35", 20: "35",
18 : "36", 18: "36",
16 : "37", 16: "37",
14 : "38", 14: "38",
12 : "39", 12: "39",
10 : "40", 10: "40",
} }
@@ -70,7 +69,7 @@ class RunzeSyringePumpConnectionError(Exception):
class RunzeSyringePumpInfo: class RunzeSyringePumpInfo:
port: str port: str
address: str = "1" address: str = "1"
max_volume: float = 25.0 max_volume: float = 25.0
mode: RunzeSyringePumpMode = RunzeSyringePumpMode.Normal mode: RunzeSyringePumpMode = RunzeSyringePumpMode.Normal
@@ -82,16 +81,16 @@ class RunzeSyringePump:
def __init__(self, port: str, address: str = "1", max_volume: float = 25.0, mode: RunzeSyringePumpMode = None): def __init__(self, port: str, address: str = "1", max_volume: float = 25.0, mode: RunzeSyringePumpMode = None):
self.port = port self.port = port
self.address = address self.address = address
self.max_volume = max_volume self.max_volume = max_volume
self.total_steps = self.total_steps_vel = 6000 self.total_steps = self.total_steps_vel = 6000
self._status = "Idle" self._status = "Idle"
self._mode = mode self._mode = mode
self._max_velocity = 0 self._max_velocity = 0
self._valve_position = "I" self._valve_position = "I"
self._position = 0 self._position = 0
try: try:
# if port in serial_ports and serial_ports[port].is_open: # if port in serial_ports and serial_ports[port].is_open:
# self.hardware_interface = serial_ports[port] # self.hardware_interface = serial_ports[port]
@@ -100,11 +99,8 @@ class RunzeSyringePump:
# baudrate=9600, # baudrate=9600,
# port=port # port=port
# ) # )
self.hardware_interface = Serial( self.hardware_interface = Serial(baudrate=9600, port=port)
baudrate=9600,
port=port
)
except (OSError, SerialException) as e: except (OSError, SerialException) as e:
# raise RunzeSyringePumpConnectionError from e # raise RunzeSyringePumpConnectionError from e
self.hardware_interface = port self.hardware_interface = port
@@ -114,13 +110,13 @@ class RunzeSyringePump:
self._error_event = Event() self._error_event = Event()
self._query_lock = Lock() self._query_lock = Lock()
self._run_lock = Lock() self._run_lock = Lock()
def _adjust_total_steps(self): def _adjust_total_steps(self):
self.total_steps = 6000 if self.mode == RunzeSyringePumpMode.Normal else 48000 self.total_steps = 6000 if self.mode == RunzeSyringePumpMode.Normal else 48000
self.total_steps_vel = 48000 if self.mode == RunzeSyringePumpMode.AccuratePosVel else 6000 self.total_steps_vel = 48000 if self.mode == RunzeSyringePumpMode.AccuratePosVel else 6000
def send_command(self, full_command: str): def send_command(self, full_command: str):
full_command_data = bytearray(full_command, 'ascii') full_command_data = bytearray(full_command, "ascii")
response = self.hardware_interface.write(full_command_data) response = self.hardware_interface.write(full_command_data)
time.sleep(0.05) time.sleep(0.05)
output = self._receive(self.hardware_interface.read_until(b"\n")) output = self._receive(self.hardware_interface.read_until(b"\n"))
@@ -131,9 +127,9 @@ class RunzeSyringePump:
if self._closing: if self._closing:
raise RunzeSyringePumpConnectionError raise RunzeSyringePumpConnectionError
run = 'R' if not "?" in command else '' run = "R" if "?" not in command else ""
full_command = f"/{self.address}{command}{run}\r\n" full_command = f"/{self.address}{command}{run}\r\n"
output = self.send_command(full_command)[3:-3] output = self.send_command(full_command)[3:-3]
return output return output
@@ -161,7 +157,7 @@ class RunzeSyringePump:
time.sleep(0.5) # Wait for 0.5 seconds before polling again time.sleep(0.5) # Wait for 0.5 seconds before polling again
status = self.get_status() status = self.get_status()
if status == 'Idle': if status == "Idle":
break break
finally: finally:
pass pass
@@ -177,7 +173,7 @@ class RunzeSyringePump:
# # self.set_mode(self.mode) # # self.set_mode(self.mode)
# self.mode = self.get_mode() # self.mode = self.get_mode()
return response return response
# Settings # Settings
def set_baudrate(self, baudrate): def set_baudrate(self, baudrate):
@@ -187,32 +183,32 @@ class RunzeSyringePump:
return self._run("U47") return self._run("U47")
else: else:
raise ValueError("Unsupported baudrate") raise ValueError("Unsupported baudrate")
# Device Status # Device Status
@property @property
def status(self) -> str: def status(self) -> str:
return self._status return self._status
def _standardize_status(self, status_raw): def _standardize_status(self, status_raw):
return "Idle" if status_raw == "`" else "Busy" return "Idle" if status_raw == "`" else "Busy"
def get_status(self): def get_status(self):
status_raw = self._query("Q") status_raw = self._query("Q")
self._status = self._standardize_status(status_raw) self._status = self._standardize_status(status_raw)
return self._status return self._status
# Mode Settings and Queries # Mode Settings and Queries
@property @property
def mode(self) -> int: def mode(self) -> int:
return self._mode return self._mode
# def set_mode(self, mode: RunzeSyringePumpMode): # def set_mode(self, mode: RunzeSyringePumpMode):
# self.mode = mode # self.mode = mode
# self._adjust_total_steps() # self._adjust_total_steps()
# command = f"N{mode.value}" # command = f"N{mode.value}"
# return self._run(command) # return self._run(command)
# def get_mode(self): # def get_mode(self):
# response = self._query("?28") # response = self._query("?28")
# status_raw, mode = response[0], int(response[1]) # status_raw, mode = response[0], int(response[1])
@@ -221,11 +217,11 @@ class RunzeSyringePump:
# return self.mode # return self.mode
# Speed Settings and Queries # Speed Settings and Queries
@property @property
def max_velocity(self) -> float: def max_velocity(self) -> float:
return self._max_velocity return self._max_velocity
def set_max_velocity(self, velocity: float): def set_max_velocity(self, velocity: float):
self._max_velocity = velocity self._max_velocity = velocity
pulse_freq = int(velocity / self.max_volume * self.total_steps_vel) pulse_freq = int(velocity / self.max_volume * self.total_steps_vel)
@@ -238,10 +234,10 @@ class RunzeSyringePump:
self._status = self._standardize_status(status_raw) self._status = self._standardize_status(status_raw)
self._max_velocity = pulse_freq / self.total_steps_vel * self.max_volume self._max_velocity = pulse_freq / self.total_steps_vel * self.max_volume
return self._max_velocity return self._max_velocity
def set_velocity_grade(self, velocity: Union[int, str]): def set_velocity_grade(self, velocity: Union[int, str]):
return self._run(f"S{velocity}") return self._run(f"S{velocity}")
def get_velocity_grade(self): def get_velocity_grade(self):
response = self._query("?2") response = self._query("?2")
status_raw, pulse_freq = response[0], int(response[1:]) status_raw, pulse_freq = response[0], int(response[1:])
@@ -265,21 +261,21 @@ class RunzeSyringePump:
self._status = self._standardize_status(status_raw) self._status = self._standardize_status(status_raw)
velocity = pulse_freq / self.total_steps_vel * self.max_volume velocity = pulse_freq / self.total_steps_vel * self.max_volume
return pulse_freq, velocity return pulse_freq, velocity
# Operations # Operations
# Valve Setpoint and Queries # Valve Setpoint and Queries
@property @property
def valve_position(self) -> str: def valve_position(self) -> str:
return self._valve_position return self._valve_position
def set_valve_position(self, position: Union[int, str, float]): def set_valve_position(self, position: Union[int, str, float]):
if type(position) == float: if isinstance(position, float):
position = round(position / 120) position = round(position / 120)
command = f"I{position}" if type(position) == int or ord(position) <= 57 else position.upper() command = f"I{position}" if isinstance(position, int) or ord(position) <= 57 else position.upper()
response = self._run(command) response = self._run(command)
self._valve_position = f"{position}" if type(position) == int or ord(position) <= 57 else position.upper() self._valve_position = f"{position}" if isinstance(position, int) or ord(position) <= 57 else position.upper()
return response return response
def get_valve_position(self) -> str: def get_valve_position(self) -> str:
@@ -288,9 +284,9 @@ class RunzeSyringePump:
self._valve_position = pos_valve self._valve_position = pos_valve
self._status = self._standardize_status(status_raw) self._status = self._standardize_status(status_raw)
return pos_valve return pos_valve
# Plunger Setpoint and Queries # Plunger Setpoint and Queries
@property @property
def position(self) -> float: def position(self) -> float:
return self._position return self._position
@@ -321,7 +317,7 @@ class RunzeSyringePump:
velocity_cmd = "" velocity_cmd = ""
pos_step = int(position / self.max_volume * self.total_steps) pos_step = int(position / self.max_volume * self.total_steps)
return self._run(f"{velocity_cmd}A{pos_step}") return self._run(f"{velocity_cmd}A{pos_step}")
def pull_plunger(self, volume: float): def pull_plunger(self, volume: float):
""" """
Pull a fixed volume (unit: ml) Pull a fixed volume (unit: ml)
@@ -334,7 +330,7 @@ class RunzeSyringePump:
""" """
pos_step = int(volume / self.max_volume * self.total_steps) pos_step = int(volume / self.max_volume * self.total_steps)
return self._run(f"P{pos_step}") return self._run(f"P{pos_step}")
def push_plunger(self, volume: float): def push_plunger(self, volume: float):
""" """
Push a fixed volume (unit: ml) Push a fixed volume (unit: ml)
@@ -355,7 +351,7 @@ class RunzeSyringePump:
def stop_operation(self): def stop_operation(self):
return self._run("T") return self._run("T")
# Queries # Queries
def query_command_buffer_status(self): def query_command_buffer_status(self):
@@ -391,4 +387,4 @@ class RunzeSyringePump:
if __name__ == "__main__": if __name__ == "__main__":
r = RunzeSyringePump("/dev/tty.usbserial-D30JUGG5", "1", 25.0) r = RunzeSyringePump("/dev/tty.usbserial-D30JUGG5", "1", 25.0)
r.initialize() r.initialize()

View File

@@ -176,6 +176,9 @@ class RunzeMultiplePump:
return output return output
def _receive(self, data: bytes) -> str: def _receive(self, data: bytes) -> str:
"""
Do not change this method.
"""
if not data: if not data:
return "" return ""
ascii_string = "".join(chr(byte) for byte in data) ascii_string = "".join(chr(byte) for byte in data)