Open
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.
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.
Comment on lines
+146
to
+148
| # 同步串口调用放入线程,避免阻塞事件循环 | ||
| await asyncio.to_thread(self.set_stir_speed, speed_int) | ||
| return True |
There was a problem hiding this comment.
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.
Suggested change
| # 同步串口调用放入线程,避免阻塞事件循环 | |
| 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
四川宜宾线下高分子组组态图完整搭建结果
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: