-
Notifications
You must be signed in to change notification settings - Fork 38
宜宾线下赛高分子组组态图构建 #122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
宜宾线下赛高分子组组态图构建 #122
Conversation
Reviewer's GuideComplete the Yibin offline high-polymer configuration by introducing IKA heater-stirrer support via a NAMUR serial client, updating registry entries for temperature and valve devices, extending resource type mappings, and wiring device_uuid propagation in ROS nodes. Sequence diagram for device_uuid propagation in serial_node initializationsequenceDiagram
participant "SerialNode"
participant "BaseROS2DeviceNode"
"SerialNode"->>"BaseROS2DeviceNode": __init__(device_uuid, driver_instance, device_id, status_types)
"SerialNode"->>"BaseROS2DeviceNode": Passes device_uuid from kwargs or generates new UUID
ER diagram for updated temperature device registryerDiagram
heaterstirrer_ika {
port string
baudrate integer
status string
stir_speed number
temp number
temp_target number
}
heaterstirrer_ika ||--o{ action_value_mappings : has
heaterstirrer_ika ||--o{ status_types : has
action_value_mappings {
auto-set_stir_speed
set_temp_target
}
status_types {
status str
stir_speed float
temp float
temp_target float
}
ER diagram for updated valve device registry (SY03B-T06)erDiagram
syringe_pump_with_valve_runze_SY03B_T06 {
fluid_port_1
fluid_port_2
fluid_port_3
fluid_port_4
fluid_port_5
fluid_port_6
}
syringe_pump_with_valve_runze_SY03B_T06 ||--o{ fluid_port_5 : has
Class diagram for new IKA heater-stirrer integrationclassDiagram
class IkaNamurClient {
+port: str
+baud: int
+timeout: float
+_ser: serial.Serial | None
+open()
+close()
+send(*tokens: str): str
+read_name(): str
+read_speed(): str
+read_speed_setpoint(): str
+set_speed(rpm: int): str
+start(): str
+stop(): str
}
class HeaterStirrer_IKA {
+_status: str
+_stir_speed: float
+_temp_target: float
+_cli: IkaNamurClient
+status: str
+stir_speed: float
+set_stir_speed(speed: float)
+temp_target: float
+set_temp_target(temp: float)
+temp: float
+start_stir(vessel, stir_speed: float, purpose: str): bool
+stop_stir(vessel): bool
+stir(stir_time: float, stir_speed: float, settling_time: float, **kwargs): bool
+close()
}
IkaNamurClient <.. HeaterStirrer_IKA : uses
Class diagram for updated resource type mapping in resource_tracker.pyclassDiagram
class ResourceDictInstance {
+res_content: ResourceContent
}
class ResourceContent {
+name: str
+uuid: str
+type: str
}
class PLRResource {
+type: str
}
ResourceDictInstance o-- ResourceContent
class ResourceTypeMap {
+TYPE_MAP: dict
}
ResourceTypeMap : plate -> Plate
ResourceTypeMap : well -> Well
ResourceTypeMap : deck -> Deck
ResourceTypeMap : container -> Container
ResourceTypeMap : resource -> Container
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey there - I've reviewed your changes - here's some feedback:
- In IkaNamurClient.send(), the loop condition
while time.time() < end or self._ser.in_waitingcould spin indefinitely if the port always shows pending bytes; consider switching to an AND condition or adding a secondary timeout to guarantee exit. - The HeaterStirrer_IKA.temp property returns the setpoint rather than an actual reading; either implement a real NAMUR query for current temperature or clearly document that it only reflects the target.
- Defaulting to a random UUID in serial_node.py on each init may break stable device identity across restarts; consider requiring a consistent device_uuid injection or persisting it externally.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In IkaNamurClient.send(), the loop condition `while time.time() < end or self._ser.in_waiting` could spin indefinitely if the port always shows pending bytes; consider switching to an AND condition or adding a secondary timeout to guarantee exit.
- The HeaterStirrer_IKA.temp property returns the setpoint rather than an actual reading; either implement a real NAMUR query for current temperature or clearly document that it only reflects the target.
- Defaulting to a random UUID in serial_node.py on each init may break stable device identity across restarts; consider requiring a consistent device_uuid injection or persisting it externally.
## Individual Comments
### Comment 1
<location> `unilabos/devices/heaterstirrer/ika.py:146-148` </location>
<code_context>
+ except (ValueError, TypeError):
+ speed_int = 0
+ # 同步串口调用放入线程,避免阻塞事件循环
+ await asyncio.to_thread(self.set_stir_speed, speed_int)
+ return True
+
+ async def stop_stir(self, vessel) -> bool:
</code_context>
<issue_to_address>
**suggestion:** Using asyncio.to_thread for synchronous serial calls is appropriate, but error handling is minimal.
Add exception handling for errors during serial communication to improve robustness.
```suggestion
# 同步串口调用放入线程,避免阻塞事件循环
try:
await asyncio.to_thread(self.set_stir_speed, speed_int)
return True
except Exception as e:
import logging
logging.error(f"Error during serial communication in set_stir_speed: {e}")
return False
```
</issue_to_address>
### Comment 2
<location> `unilabos/devices/heaterstirrer/ika.py:49-50` </location>
<code_context>
def send(self, *tokens: str) -> str:
if not self._ser or not self._ser.is_open:
self.open()
line = " ".join(tokens).upper()
payload = (line + "\r\n").encode("ascii")
self._ser.reset_input_buffer()
self._ser.write(payload)
time.sleep(0.25)
buf = bytearray()
end = time.time() + self.timeout
while time.time() < end or self._ser.in_waiting:
data = self._ser.read(self._ser.in_waiting or 1)
if data:
buf.extend(data)
else:
time.sleep(0.02)
return buf.decode("ascii", errors="ignore").strip()
</code_context>
<issue_to_address>
**suggestion (code-quality):** Use named expression to simplify assignment and conditional ([`use-named-expression`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/use-named-expression/))
```suggestion
if data := self._ser.read(self._ser.in_waiting or 1):
```
</issue_to_address>
### Comment 3
<location> `unilabos/devices/heaterstirrer/ika.py:106` </location>
<code_context>
def set_stir_speed(self, speed: float):
speed_int = int(float(speed))
self._cli.set_speed(speed_int)
if speed_int > 0:
self._cli.start()
else:
self._cli.stop()
self._stir_speed = float(speed_int)
</code_context>
<issue_to_address>
**suggestion (code-quality):** Remove unnecessary casts to int, str, float or bool ([`remove-unnecessary-cast`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/remove-unnecessary-cast/))
```suggestion
speed_int = int(speed)
```
</issue_to_address>
### Comment 4
<location> `unilabos/devices/heaterstirrer/ika.py:119` </location>
<code_context>
def set_temp_target(self, temp: float):
self._temp_target = float(temp)
self._cli.send("OUT_SP_1", f"{int(self._temp_target)}")
self._cli.send("START_1")
</code_context>
<issue_to_address>
**suggestion (code-quality):** Remove unnecessary casts to int, str, float or bool ([`remove-unnecessary-cast`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/remove-unnecessary-cast/))
```suggestion
self._temp_target = temp
```
</issue_to_address>
### Comment 5
<location> `unilabos/devices/heaterstirrer/ika.py:130-132` </location>
<code_context>
def _extract_vessel_id(self, vessel) -> str:
if isinstance(vessel, dict):
return str(vessel.get("id", ""))
return str(vessel)
</code_context>
<issue_to_address>
**suggestion (code-quality):** We've found these issues:
- Lift code into else after jump in control flow ([`reintroduce-else`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/reintroduce-else/))
- Replace if statement with if expression ([`assign-if-exp`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/assign-if-exp/))
```suggestion
return str(vessel.get("id", "")) if isinstance(vessel, dict) else str(vessel)
```
</issue_to_address>
### Comment 6
<location> `unilabos/devices/heaterstirrer/ika.py:143` </location>
<code_context>
async def start_stir(self, vessel, stir_speed: float, purpose: str = "") -> bool:
"""开始持续搅拌(协议动作)
- vessel: 可为字符串或形如 {"id": "..."} 的字典
- stir_speed: 目标转速 RPM
- purpose: 可选用途描述(仅用于上层日志)
"""
_ = self._extract_vessel_id(vessel) # 当前实现不强依赖容器,仅做形参兼容
try:
speed_int = int(float(stir_speed))
except (ValueError, TypeError):
speed_int = 0
# 同步串口调用放入线程,避免阻塞事件循环
await asyncio.to_thread(self.set_stir_speed, speed_int)
return True
</code_context>
<issue_to_address>
**suggestion (code-quality):** Remove unnecessary casts to int, str, float or bool ([`remove-unnecessary-cast`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/remove-unnecessary-cast/))
```suggestion
speed_int = int(stir_speed)
```
</issue_to_address>
### Comment 7
<location> `unilabos/devices/heaterstirrer/ika.py:165-173` </location>
<code_context>
async def stir(self, stir_time: float, stir_speed: float, settling_time: float, **kwargs) -> bool:
"""定时搅拌 + 沉降(协议动作)
- stir_time: 搅拌时间(秒)
- stir_speed: 搅拌速度(RPM)
- settling_time: 沉降时间(秒)
其余 kwargs(如 vessel/time/time_spec/event)按协议形态传入,此处可忽略。
"""
try:
total_stir_seconds = max(0.0, float(stir_time))
except (ValueError, TypeError):
total_stir_seconds = 0.0
try:
speed_int = int(float(stir_speed))
except (ValueError, TypeError):
speed_int = 0
try:
total_settle_seconds = max(0.0, float(settling_time))
except (ValueError, TypeError):
total_settle_seconds = 0.0
# 开始搅拌
await asyncio.to_thread(self.set_stir_speed, speed_int)
if total_stir_seconds > 0:
await asyncio.sleep(total_stir_seconds)
# 停止搅拌进入沉降
await asyncio.to_thread(self.set_stir_speed, 0)
if total_settle_seconds > 0:
await asyncio.sleep(total_settle_seconds)
return True
</code_context>
<issue_to_address>
**issue (code-quality):** Remove unnecessary casts to int, str, float or bool [×3] ([`remove-unnecessary-cast`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/remove-unnecessary-cast/))
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| # 同步串口调用放入线程,避免阻塞事件循环 | ||
| await asyncio.to_thread(self.set_stir_speed, speed_int) | ||
| return True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: Using asyncio.to_thread for synchronous serial calls is appropriate, but error handling is minimal.
Add exception handling for errors during serial communication to improve robustness.
| # 同步串口调用放入线程,避免阻塞事件循环 | |
| await asyncio.to_thread(self.set_stir_speed, speed_int) | |
| return True | |
| # 同步串口调用放入线程,避免阻塞事件循环 | |
| try: | |
| await asyncio.to_thread(self.set_stir_speed, speed_int) | |
| return True | |
| except Exception as e: | |
| import logging | |
| logging.error(f"Error during serial communication in set_stir_speed: {e}") | |
| return False |
四川宜宾线下高分子组组态图完整搭建结果
Summary by Sourcery
Add support for IKA heater‐stirrer devices via NAMUR protocol and update device and resource registries to handle new mappings and identifiers
New Features:
Tests: