Skip to content

Conversation

@xiaoyu10031
Copy link
Collaborator

@xiaoyu10031 xiaoyu10031 commented Dec 18, 2025

由 Sourcery 提供的摘要

在移除若干旧版设备定义的同时,新增 SII 设备支持。

新功能:

  • 在设备注册表中新增 SII 设备条目,包括支持视频流和 PTZ 控制的 USB 和 WiFi 相机,以及支持运动与液体处理指令的 XYZ 移液工作站。

增强:

  • 实现共享 RS485 总线、基于 Modbus 的 XYZ 步进电机控制器和 SOPA 移液驱动,同时增加工作站编排逻辑,以及用于运动控制、移液操作和点位管理的示例/演示脚本。
  • 新增 RTSP/WiFi 和 USB 相机控制器,支持 WebSocket 信令、FFmpeg RTMP 推流和 WebRTC offer 处理,并提供 PTZ 控制工具与测试脚本。

杂项:

  • 移除已弃用且不再需要的 HPLC、Raman、OPC UA 示例以及 iCL42 电机设备注册表条目。
Original summary in English

Summary by Sourcery

Add SII device support while removing several legacy device definitions.

New Features:

  • Introduce SII device registry entries for USB and WiFi cameras with streaming and PTZ control, and for an XYZ pipette station with motion and liquid-handling commands.

Enhancements:

  • Implement shared RS485 bus, Modbus-based XYZ stepper controller, and SOPA pipette driver, along with station orchestration and sample/demo scripts for motion, pipetting, and point management.
  • Add camera controllers for RTSP/WiFi and USB cameras with WebSocket signaling, FFmpeg RTMP streaming, and WebRTC offer handling, plus PTZ control utilities and test scripts.

Chores:

  • Remove deprecated HPLC, Raman, OPC UA example, and iCL42 motor device registry entries that are no longer needed.

@sourcery-ai
Copy link

sourcery-ai bot commented Dec 18, 2025

审阅者指南

引入全新的 SII 设备家族(USB 相机、WiFi/PTZ 相机,以及 Laiyu XYZ 移液工作站),提供完整的驱动实现和注册表条目,同时移除若干已不再使用的传统设备注册定义。

启动 WiFi/PTZ 相机推流的时序图

sequenceDiagram
  participant Orchestrator as UniLabOS_device_runtime
  participant Camera as CameraController_Wifi
  participant FFmpeg as FFmpeg_process
  participant WS as Signal_backend_ws
  participant SRS as SRS_media_server
  participant IPCam as IP_camera_RTSP

  Orchestrator->>Camera: start(config)
  activate Camera
  Camera->>Camera: apply_config_and_init_ptz()
  Camera->>FFmpeg: spawn_process(rtsp=self.camera_rtsp_url, out=self.rtmp_url)
  Note over FFmpeg,IPCam: FFmpeg pulls RTSP from IPCam and pushes RTMP to SRS
  Camera->>WS: open_websocket(signal_backend_url)
  Camera->>Camera: start_event_loop_thread()
  Camera-->>Orchestrator: {status: started, urls...}
  deactivate Camera

  WS-->>Camera: {command: start_stream}
  activate Camera
  Camera->>Camera: _start_ffmpeg() (idempotent)
  deactivate Camera

  WS-->>Camera: {type: offer, sdp, cameraId}
  activate Camera
  Camera->>SRS: HTTP POST webrtc_api {api, streamurl, sdp}
  SRS-->>Camera: {sdp: answer_sdp}
  Camera-->>WS: {type: answer, sdp: answer_sdp, cameraId, hostId}
  deactivate Camera
Loading

XYZ 移液站移动到目标点并保存该点的时序图

sequenceDiagram
  participant Runtime as UniLabOS_device_runtime
  participant Station as Station
  participant XYZ as XYZStepperController
  participant Pipette as SOPAPipetteYYQ
  participant Bus as SharedRS485Bus
  participant Axes as XYZ_stepper_axes

  Runtime->>Station: move_to_point(name, speed, acc, z_offset)
  activate Station
  Station->>Station: _ensure_connected()
  Station->>Station: lookup points[name]
  Station->>Station: move_xyz(x, y, z+z_offset, speed, acc)
  Station->>XYZ: move_xyz_work(x, y, z+z_offset, speed, acc)
  activate XYZ
  XYZ->>XYZ: compute_machine_steps_from_work_origin()
  XYZ->>Bus: write_modbus_frames(target_steps)
  Bus->>Axes: drive_stepper_motors()
  XYZ->>XYZ: wait_complete(X,Y,Z)
  deactivate XYZ
  Station-->>Runtime: move_to_point done
  deactivate Station

  Runtime->>Station: save_current_position_as_point(name)
  activate Station
  Station->>Station: read _last_target_work[x,y,z]
  Station->>Station: save_point(name, x, y, z)
  Station->>Station: update points[name]
  Station-->>Runtime: save_current_position_as_point done
  deactivate Station
Loading

SII 相机驱动和 PTZ 控制器的类图

classDiagram
  class CameraController_USB {
    +str host_id
    +str signal_backend_url
    +str rtmp_url
    +str webrtc_api
    +str webrtc_stream_url
    +str video_device
    +int width
    +int height
    +int fps
    +str video_bitrate
    +str audio_device
    +str audio_bitrate
    -object _ws
    -Popen _ffmpeg_process
    -bool _running
    -bool _websocket_connected
    -Future _loop_task
    -AbstractEventLoop _loop
    -Thread _loop_thread
    +start(config Dict) Dict
    +stop() Dict
    +get_status() Dict
    +running bool
    +websocket_connected bool
    +ffmpeg_running bool
    -_run_main_loop() void
    -_recv_loop() void
    -_handle_message(data Dict) void
    -_start_ffmpeg() void
    -_stop_ffmpeg() void
    -_handle_webrtc_offer(offer_sdp str) str
  }

  class PTZController {
    +str host
    +int port
    +str user
    +str password
    +ONVIFCamera cam
    +object media_service
    +object ptz_service
    +object profile
    +connect() bool
    -_continuous_move(pan float, tilt float, zoom float, duration float) bool
    +stop() bool
    +move_up(speed float, duration float) bool
    +move_down(speed float, duration float) bool
    +move_left(speed float, duration float) bool
    +move_right(speed float, duration float) bool
    +zoom_in(speed float, duration float) bool
    +zoom_out(speed float, duration float) bool
    -_force_stop(retries int, delay float) bool
  }

  class CameraController_Wifi {
    +str host_id
    +str signal_backend_url
    +str rtmp_url
    +str webrtc_api
    +str webrtc_stream_url
    +str camera_rtsp_url
    +str ptz_host
    +int ptz_port
    +str ptz_user
    +str ptz_password
    -PTZController _ptz
    -object _ws
    -Popen _ffmpeg_process
    -bool _running
    -Future _loop_task
    -AbstractEventLoop _loop
    -Thread _loop_thread
    +start(config Dict) Dict
    +stop() Dict
    +get_status() Dict
    +ptz_move_up(speed float, duration float) bool
    +ptz_move_down(speed float, duration float) bool
    +ptz_move_left(speed float, duration float) bool
    +ptz_move_right(speed float, duration float) bool
    +zoom_in(speed float, duration float) bool
    +zoom_out(speed float, duration float) bool
    +ptz_stop() void
    -_init_ptz_if_possible() void
    -_run_main_loop() void
    -_recv_loop() void
    -_handle_message(data Dict) void
    -_start_ffmpeg() void
    -_stop_ffmpeg() void
    -_handle_webrtc_offer(offer_sdp str) str
  }

  CameraController_Wifi --> PTZController : uses
Loading

SII Laiyu XYZ 移液工作站及驱动的类图

classDiagram
  class SharedRS485Bus {
    +str port
    +int baudrate
    +float timeout
    +Serial serial
    +Lock lock
    +open() bool
    +close() void
    +reset_input() void
    +write(data bytes) void
    +read(n int) bytes
    +read_exact(n int, overall_timeout float) bytes
  }

  class XYZModbus {
    +SharedRS485Bus bus
    +bool ignore_crc_error
    +set_ignore_crc(flag bool) void
    +read_regs(slave int, addr int, count int) List~int~
    +write_reg(slave int, addr int, val int) bool
    +write_regs(slave int, start int, values List~int~) bool
    -_crc16(data bytes) bytes
    -_xfer(slave int, payload bytes, retries int, delay_before_read float) bytes
  }

  class MotorStatus {
    <<enum>>
    STANDBY
    RUNNING
    COLLISION_STOP
    FORWARD_LIMIT_STOP
    FORWARD_LIMIT_STOP
    REVERSE_LIMIT_STOP
  }

  class XYZStepperController {
    +SharedRS485Bus bus
    +XYZModbus modbus
    +Dict axis_addr
    +Dict work_origin_steps
    +bool is_homed
    +get_status(axis str) List~int~
    +enable(axis str, state bool) bool
    +wait_complete(axis str, timeout float) bool
    +move_to(axis str, steps int, speed int, acc int, precision int) bool
    +move_xyz_work(x float, y float, z float, speed int, acc int) void
    +mm_to_steps(axis str, mm float) int
    +steps_to_mm(axis str, steps int) float
    +define_current_as_zero(save_path str) void
    +return_to_work_origin(speed int, acc int) void
    -_to_machine_steps(axis str, mm float) int
    -_load_work_origin(path str) bool
    -s16(v int) int
    -s32(h int, l int) int
  }

  class SOPAConfig {
    +int address
    +float timeout
  }

  class SOPAPipetteYYQ {
    +SharedRS485Bus bus
    +SOPAConfig config
    +initialize() bool
    +eject_tip() bool
    +aspirate(volume_uL float) void
    +dispense(volume_uL float) void
    +set_max_speed(speed int) void
    +set_start_speed(speed int) void
    +set_cutoff_speed(speed int) void
    +set_acceleration(accel int) void
    +get_status() str
    +get_tip_status() bool
    -_send_and_read(cmd str, delay_before_read float) str
  }

  class Station {
    +str port
    +int baudrate
    +SharedRS485Bus bus
    +XYZStepperController xyz
    +SOPAPipetteYYQ pip
    +str points_file
    +str origin_file
    +Dict points
    +Dict _last_target_work
    +connect() void
    +disconnect() void
    +move_xyz(x float, y float, z float, speed int, acc int) void
    +move_to_point(name str, speed int, acc int, z_offset float) void
    +aspirate(volume_uL float) void
    +dispense(volume_uL float) void
    +eject_tip() void
    +save_point(name str, x float, y float, z float) void
    +save_current_position_as_point(name str) void
    +define_current_as_zero() void
    -_load_points() void
    -_ensure_connected() void
  }

  SharedRS485Bus <.. XYZModbus : uses
  XYZStepperController --> XYZModbus
  XYZStepperController --> MotorStatus
  SOPAPipetteYYQ --> SharedRS485Bus
  Station --> SharedRS485Bus
  Station --> XYZStepperController
  Station --> SOPAPipetteYYQ
Loading

文件级变更

Change Details Files
在 UniLabOS 设备注册表中注册新的 SII 设备(USB 相机、WiFi/PTZ 相机控制器、Laiyu XYZ 移液工作站),并配置相应的操作和状态类型。
  • 新增 camera_usb 设备,提供 start/stop/get_status 操作和与流媒体相关的状态字段,映射到用于 Linux USB 相机的 Python CameraController 实现。
  • 新增用于 WiFi/PTZ 相机的 cameracontroller_device,暴露 PTZ、变焦和推流 start/stop 操作,绑定到使用 RTSP、WebSocket 信令和 ONVIF PTZ 的 CameraController 实现。
  • 新增 xyz_pipette_device 条目,暴露连接/断开、XYZ 运动、点位管理和移液操作,并映射到使用共享 RS485 总线和 Laiyu 硬件的 Station 驱动。
unilabos/registry/devices/SII.yaml
实现集成 FFmpeg 推流、WebSocket 信令和 ONVIF PTZ 控制的 WiFi/RTSP 相机控制器。
  • 创建 PTZController 类,对 ONVIFCamera 进行封装,实现带安全停止的连续 PTZ 运动以及基本变焦操作桩。
  • 实现面向 RTSP 相机的 CameraController,负责管理 FFmpeg RTMP 推流、WebSocket 信令循环,以及通过 HTTP API 处理 WebRTC offer/answer,并提供映射自注册表操作的 PTZ 运动辅助方法。
  • 新增演示/测试脚本,用于仅 PTZ 测试、CameraController+PTZ 联合测试、RTSP 帧抓取,以及相机推流状态监控。
unilabos/devices/SII/cameraSII/cameraWifi.py
unilabos/devices/SII/cameraSII/ptz_test.py
unilabos/devices/SII/cameraSII/ptz_cameracontroller_test.py
unilabos/devices/SII/cameraSII/demo_camera_pic.py
unilabos/devices/SII/cameraSII/demo_camera_push.py
实现基于 FFmpeg 的 Linux USB 相机控制器,支持 RTMP 推流和 WebSocket/WebRTC 信令,并提供与注册表模式兼容的状态上报。
  • 创建面向 USB 相机的 CameraController,使用 V4L2 作为视频输入、可选 ALSA 音频采集,并支持可配置的分辨率/FPS/码率。
  • 集成后台 asyncio 事件循环和 WebSocket 客户端,用于处理来自信令后端的 start/stop 推流命令和 WebRTC offer。
  • 通过属性和 get_status() 暴露状态,满足注册表中 ffmpeg_running / running / websocket_connected 字段的期望。
  • 新增简单的 cameraUSB_test 脚本,实例化控制器并使用典型配置,周期性打印状态。
unilabos/devices/SII/cameraSII/cameraUSB.py
unilabos/devices/SII/cameraSII/cameraUSB_test.py
引入共享 RS485 总线、Modbus 电机驱动、XYZ 步进控制器和 SOPA 移液驱动,以支持 Laiyu XYZ 移液工作站抽象。
  • 创建 SharedRS485Bus 抽象,提供线程安全访问和基本读写工具,用于电机与移液器共享的 RS485 串口通信。
  • 实现 XYZModbus,包含 CRC16、带重试的健壮帧解析,以及针对现有电机寄存器映射的单寄存器/多寄存器读写辅助方法。
  • XYZModbus 之上实现 XYZStepperController,用于管理 XYZ 各轴、完成 mm 与步数之间的转换、执行安全的 Z 抬升 / XY 移动 / Z 下探、管理工作原点软零点以及回原点操作。
  • 创建 SOPAPipetteYYQ 驱动,通过共享总线使用 SOPA 文本协议通信,提供吸液、排液、弹头、速度/加速度配置及状态查询等能力。
unilabos/devices/SII/laiyu_xyz_pipette/drivers/SharedRS485Bus.py
unilabos/devices/SII/laiyu_xyz_pipette/drivers/XYZModbus.py
unilabos/devices/SII/laiyu_xyz_pipette/XYZStepperController.py
unilabos/devices/SII/laiyu_xyz_pipette/drivers/SOPAPipetteYYQ.py
提供围绕 XYZ 和移液驱动的更高层 Station 抽象,包括点位管理以及便捷的演示/测试脚本。
  • 实现 Station 类,统一管理 SharedRS485BusXYZStepperControllerSOPAPipetteYYQ,对外提供 connect/disconnect、move_xyz、带 z_offsetmove_to_point、aspirate/dispense/eject_tip,以及工作原点管理接口。
  • 实现点位管理:加载 points.json,保存/更新点位,并通过 save_current_position_as_point 使用最近一次下发的工作坐标,而非直接读取电机状态,从而避免对直接状态读出的依赖。
  • 新增演示工具,用于清洗/排空/弹出移液枪头、移动到带 z_offset 的命名点位、基础 XYZ 运动,以及用于从站扫描和打印当前 XYZ 步数的辅助脚本。
  • 新增默认的 points.jsonwork_origin.json 占位文件,为 StationXYZStepperController 使用的点位/原点持久化提供支持。
unilabos/devices/SII/laiyu_xyz_pipette/station.py
unilabos/devices/SII/laiyu_xyz_pipette/demo_pipette.py
unilabos/devices/SII/laiyu_xyz_pipette/demo_points.py
unilabos/devices/SII/laiyu_xyz_pipette/demo_xyz.py
unilabos/devices/SII/laiyu_xyz_pipette/test_save_points_x.py
unilabos/devices/SII/laiyu_xyz_pipette/test_save_points_y.py
unilabos/devices/SII/laiyu_xyz_pipette/test_scan_slaves.py
unilabos/devices/SII/laiyu_xyz_pipette/print_xyz_steps.py
unilabos/devices/SII/laiyu_xyz_pipette/points.json
unilabos/devices/SII/laiyu_xyz_pipette/work_origin.json
清理已废弃的 HPLC、拉曼、OPC UA 示例以及 iCL42 电机设备的注册表条目。
  • characterization_chromatic 注册表中移除详细的 hplc.agilent 设备定义,仅保留 hplc.agilent-zhida 条目。
  • raman.home_madeopcua_example 注册表内容替换为空映射(YAML 空对象),实质上禁用这些示例设备。
  • robot_linear_motion 注册表中移除 motor.iCL42 定义,包括其 UniLabJsonCommand 操作以及 init/data 模式。
unilabos/registry/devices/characterization_chromatic.yaml
unilabos/registry/devices/characterization_optic.yaml
unilabos/registry/devices/opcua_example.yaml
unilabos/registry/devices/robot_linear_motion.yaml

技巧和命令

与 Sourcery 交互

  • 触发新的审查: 在 Pull Request 中评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的审查评论。
  • 从审查评论生成 GitHub issue: 在某条审查评论下回复,请求 Sourcery 从该评论创建 issue。你也可以在评论中回复 @sourcery-ai issue,从该评论创建 issue。
  • 生成 Pull Request 标题: 在 Pull Request 标题中任意位置写上 @sourcery-ai,即可随时生成标题。你也可以在 Pull Request 中评论 @sourcery-ai title 来(重新)生成标题。
  • 生成 Pull Request 总结: 在 Pull Request 正文任意位置写上 @sourcery-ai summary,即可在该位置生成 PR 总结。你也可以评论 @sourcery-ai summary 来在任意时间(重新)生成总结。
  • 生成审阅者指南: 在 Pull Request 中评论 @sourcery-ai guide,即可随时(重新)生成审阅者指南。
  • 一次性解决所有 Sourcery 评论: 在 Pull Request 中评论 @sourcery-ai resolve,即可将所有 Sourcery 评论标记为已解决。适用于你已经处理完所有评论且不希望再看到它们的场景。
  • 一次性忽略所有 Sourcery 审查: 在 Pull Request 中评论 @sourcery-ai dismiss,即可忽略所有现有的 Sourcery 审查。特别适用于你希望从一次全新的审查开始——别忘了随后评论 @sourcery-ai review 来触发新的审查!

自定义使用体验

打开你的 控制面板 以:

  • 启用或禁用审查功能,例如 Sourcery 自动生成的 Pull Request 总结、审阅者指南等。
  • 更改审查语言。
  • 添加、移除或编辑自定义审查说明。
  • 调整其他审查相关设置。

获取帮助

Original review guide in English

Reviewer's Guide

Introduce a new SII device family (USB camera, WiFi/PTZ camera, and Laiyu XYZ pipette station) with full driver implementations and registry entries, while removing several legacy device registry definitions that are no longer used.

Sequence diagram for starting WiFi/PTZ camera streaming

sequenceDiagram
  participant Orchestrator as UniLabOS_device_runtime
  participant Camera as CameraController_Wifi
  participant FFmpeg as FFmpeg_process
  participant WS as Signal_backend_ws
  participant SRS as SRS_media_server
  participant IPCam as IP_camera_RTSP

  Orchestrator->>Camera: start(config)
  activate Camera
  Camera->>Camera: apply_config_and_init_ptz()
  Camera->>FFmpeg: spawn_process(rtsp=self.camera_rtsp_url, out=self.rtmp_url)
  Note over FFmpeg,IPCam: FFmpeg pulls RTSP from IPCam and pushes RTMP to SRS
  Camera->>WS: open_websocket(signal_backend_url)
  Camera->>Camera: start_event_loop_thread()
  Camera-->>Orchestrator: {status: started, urls...}
  deactivate Camera

  WS-->>Camera: {command: start_stream}
  activate Camera
  Camera->>Camera: _start_ffmpeg() (idempotent)
  deactivate Camera

  WS-->>Camera: {type: offer, sdp, cameraId}
  activate Camera
  Camera->>SRS: HTTP POST webrtc_api {api, streamurl, sdp}
  SRS-->>Camera: {sdp: answer_sdp}
  Camera-->>WS: {type: answer, sdp: answer_sdp, cameraId, hostId}
  deactivate Camera
Loading

Sequence diagram for moving to a point and saving it in XYZ pipette station

sequenceDiagram
  participant Runtime as UniLabOS_device_runtime
  participant Station as Station
  participant XYZ as XYZStepperController
  participant Pipette as SOPAPipetteYYQ
  participant Bus as SharedRS485Bus
  participant Axes as XYZ_stepper_axes

  Runtime->>Station: move_to_point(name, speed, acc, z_offset)
  activate Station
  Station->>Station: _ensure_connected()
  Station->>Station: lookup points[name]
  Station->>Station: move_xyz(x, y, z+z_offset, speed, acc)
  Station->>XYZ: move_xyz_work(x, y, z+z_offset, speed, acc)
  activate XYZ
  XYZ->>XYZ: compute_machine_steps_from_work_origin()
  XYZ->>Bus: write_modbus_frames(target_steps)
  Bus->>Axes: drive_stepper_motors()
  XYZ->>XYZ: wait_complete(X,Y,Z)
  deactivate XYZ
  Station-->>Runtime: move_to_point done
  deactivate Station

  Runtime->>Station: save_current_position_as_point(name)
  activate Station
  Station->>Station: read _last_target_work[x,y,z]
  Station->>Station: save_point(name, x, y, z)
  Station->>Station: update points[name]
  Station-->>Runtime: save_current_position_as_point done
  deactivate Station
Loading

Class diagram for SII camera drivers and PTZ controller

classDiagram
  class CameraController_USB {
    +str host_id
    +str signal_backend_url
    +str rtmp_url
    +str webrtc_api
    +str webrtc_stream_url
    +str video_device
    +int width
    +int height
    +int fps
    +str video_bitrate
    +str audio_device
    +str audio_bitrate
    -object _ws
    -Popen _ffmpeg_process
    -bool _running
    -bool _websocket_connected
    -Future _loop_task
    -AbstractEventLoop _loop
    -Thread _loop_thread
    +start(config Dict) Dict
    +stop() Dict
    +get_status() Dict
    +running bool
    +websocket_connected bool
    +ffmpeg_running bool
    -_run_main_loop() void
    -_recv_loop() void
    -_handle_message(data Dict) void
    -_start_ffmpeg() void
    -_stop_ffmpeg() void
    -_handle_webrtc_offer(offer_sdp str) str
  }

  class PTZController {
    +str host
    +int port
    +str user
    +str password
    +ONVIFCamera cam
    +object media_service
    +object ptz_service
    +object profile
    +connect() bool
    -_continuous_move(pan float, tilt float, zoom float, duration float) bool
    +stop() bool
    +move_up(speed float, duration float) bool
    +move_down(speed float, duration float) bool
    +move_left(speed float, duration float) bool
    +move_right(speed float, duration float) bool
    +zoom_in(speed float, duration float) bool
    +zoom_out(speed float, duration float) bool
    -_force_stop(retries int, delay float) bool
  }

  class CameraController_Wifi {
    +str host_id
    +str signal_backend_url
    +str rtmp_url
    +str webrtc_api
    +str webrtc_stream_url
    +str camera_rtsp_url
    +str ptz_host
    +int ptz_port
    +str ptz_user
    +str ptz_password
    -PTZController _ptz
    -object _ws
    -Popen _ffmpeg_process
    -bool _running
    -Future _loop_task
    -AbstractEventLoop _loop
    -Thread _loop_thread
    +start(config Dict) Dict
    +stop() Dict
    +get_status() Dict
    +ptz_move_up(speed float, duration float) bool
    +ptz_move_down(speed float, duration float) bool
    +ptz_move_left(speed float, duration float) bool
    +ptz_move_right(speed float, duration float) bool
    +zoom_in(speed float, duration float) bool
    +zoom_out(speed float, duration float) bool
    +ptz_stop() void
    -_init_ptz_if_possible() void
    -_run_main_loop() void
    -_recv_loop() void
    -_handle_message(data Dict) void
    -_start_ffmpeg() void
    -_stop_ffmpeg() void
    -_handle_webrtc_offer(offer_sdp str) str
  }

  CameraController_Wifi --> PTZController : uses
Loading

Class diagram for SII Laiyu XYZ pipette station and drivers

classDiagram
  class SharedRS485Bus {
    +str port
    +int baudrate
    +float timeout
    +Serial serial
    +Lock lock
    +open() bool
    +close() void
    +reset_input() void
    +write(data bytes) void
    +read(n int) bytes
    +read_exact(n int, overall_timeout float) bytes
  }

  class XYZModbus {
    +SharedRS485Bus bus
    +bool ignore_crc_error
    +set_ignore_crc(flag bool) void
    +read_regs(slave int, addr int, count int) List~int~
    +write_reg(slave int, addr int, val int) bool
    +write_regs(slave int, start int, values List~int~) bool
    -_crc16(data bytes) bytes
    -_xfer(slave int, payload bytes, retries int, delay_before_read float) bytes
  }

  class MotorStatus {
    <<enum>>
    STANDBY
    RUNNING
    COLLISION_STOP
    FORWARD_LIMIT_STOP
    FORWARD_LIMIT_STOP
    REVERSE_LIMIT_STOP
  }

  class XYZStepperController {
    +SharedRS485Bus bus
    +XYZModbus modbus
    +Dict axis_addr
    +Dict work_origin_steps
    +bool is_homed
    +get_status(axis str) List~int~
    +enable(axis str, state bool) bool
    +wait_complete(axis str, timeout float) bool
    +move_to(axis str, steps int, speed int, acc int, precision int) bool
    +move_xyz_work(x float, y float, z float, speed int, acc int) void
    +mm_to_steps(axis str, mm float) int
    +steps_to_mm(axis str, steps int) float
    +define_current_as_zero(save_path str) void
    +return_to_work_origin(speed int, acc int) void
    -_to_machine_steps(axis str, mm float) int
    -_load_work_origin(path str) bool
    -s16(v int) int
    -s32(h int, l int) int
  }

  class SOPAConfig {
    +int address
    +float timeout
  }

  class SOPAPipetteYYQ {
    +SharedRS485Bus bus
    +SOPAConfig config
    +initialize() bool
    +eject_tip() bool
    +aspirate(volume_uL float) void
    +dispense(volume_uL float) void
    +set_max_speed(speed int) void
    +set_start_speed(speed int) void
    +set_cutoff_speed(speed int) void
    +set_acceleration(accel int) void
    +get_status() str
    +get_tip_status() bool
    -_send_and_read(cmd str, delay_before_read float) str
  }

  class Station {
    +str port
    +int baudrate
    +SharedRS485Bus bus
    +XYZStepperController xyz
    +SOPAPipetteYYQ pip
    +str points_file
    +str origin_file
    +Dict points
    +Dict _last_target_work
    +connect() void
    +disconnect() void
    +move_xyz(x float, y float, z float, speed int, acc int) void
    +move_to_point(name str, speed int, acc int, z_offset float) void
    +aspirate(volume_uL float) void
    +dispense(volume_uL float) void
    +eject_tip() void
    +save_point(name str, x float, y float, z float) void
    +save_current_position_as_point(name str) void
    +define_current_as_zero() void
    -_load_points() void
    -_ensure_connected() void
  }

  SharedRS485Bus <.. XYZModbus : uses
  XYZStepperController --> XYZModbus
  XYZStepperController --> MotorStatus
  SOPAPipetteYYQ --> SharedRS485Bus
  Station --> SharedRS485Bus
  Station --> XYZStepperController
  Station --> SOPAPipetteYYQ
Loading

File-Level Changes

Change Details Files
Register new SII devices (USB camera, WiFi/PTZ camera controller, Laiyu XYZ pipette station) in UniLabOS device registry with appropriate actions and status types.
  • Add camera_usb device with start/stop/get_status actions and streaming-related status fields mapped to a Python CameraController implementation for Linux USB cameras.
  • Add cameracontroller_device for WiFi/PTZ camera with PTZ, zoom and streaming start/stop actions pointing to a CameraController implementation using RTSP, WebSocket signaling, and ONVIF PTZ.
  • Add xyz_pipette_device entry exposing connect/disconnect, XYZ move, point management, and pipetting actions mapped to a Station driver using shared RS485 bus and Laiyu hardware.
unilabos/registry/devices/SII.yaml
Implement WiFi/RTSP camera controller with integrated FFmpeg streaming, WebSocket signaling, and ONVIF PTZ control.
  • Create PTZController class wrapping ONVIFCamera for continuous PTZ moves with safe stop and basic zoom stubs.
  • Implement CameraController for RTSP cameras that manages FFmpeg RTMP pushing, WebSocket signaling loop, and WebRTC offer/answer handling via an HTTP API, plus PTZ move helper methods mapped from registry actions.
  • Add demo/test scripts for PTZ-only testing, combined CameraController PTZ testing, RTSP frame capture, and camera push status monitoring.
unilabos/devices/SII/cameraSII/cameraWifi.py
unilabos/devices/SII/cameraSII/ptz_test.py
unilabos/devices/SII/cameraSII/ptz_cameracontroller_test.py
unilabos/devices/SII/cameraSII/demo_camera_pic.py
unilabos/devices/SII/cameraSII/demo_camera_push.py
Implement Linux USB camera controller with FFmpeg-based RTMP streaming and WebSocket/WebRTC signaling, plus status reporting compatible with the registry schema.
  • Create CameraController for USB cameras using V4L2 input, optional ALSA audio capture, and configurable resolution/FPS/bitrates.
  • Integrate a background asyncio event loop and WebSocket client to handle start/stop stream commands and WebRTC offers from a signaling backend.
  • Expose status via properties and get_status() matching ffmpeg_running/running/websocket_connected fields expected by the registry.
  • Add a simple cameraUSB_test script that instantiates the controller with typical configuration and periodically prints status.
unilabos/devices/SII/cameraSII/cameraUSB.py
unilabos/devices/SII/cameraSII/cameraUSB_test.py
Introduce shared RS485 bus, Modbus motor driver, XYZ stepper controller, and SOPA pipette driver to support the Laiyu XYZ pipette station abstraction.
  • Create SharedRS485Bus abstraction with thread-safe access and basic read/write utilities for RS485 serial communication shared by motors and pipette.
  • Implement XYZModbus with CRC16, robust frame parsing with retries, and read/write single/multiple register helpers tailored to the existing motor register map.
  • Implement XYZStepperController on top of XYZModbus to manage XYZ axes, convert between mm and steps, perform safe Z-up/XY/Z-down moves, manage work-origin soft zero, and return-to-origin operations.
  • Create SOPAPipetteYYQ driver that speaks SOPA text protocol over the shared bus, providing aspirate, dispense, eject_tip, speed/accel configuration, and status queries.
unilabos/devices/SII/laiyu_xyz_pipette/drivers/SharedRS485Bus.py
unilabos/devices/SII/laiyu_xyz_pipette/drivers/XYZModbus.py
unilabos/devices/SII/laiyu_xyz_pipette/XYZStepperController.py
unilabos/devices/SII/laiyu_xyz_pipette/drivers/SOPAPipetteYYQ.py
Provide a higher-level Station abstraction around the XYZ and pipette drivers, including point management and convenience demos/tests.
  • Implement Station class that owns SharedRS485Bus, XYZStepperController, and SOPAPipetteYYQ, exposing connect/disconnect, move_xyz, move_to_point with z_offset, aspirate/dispense/eject_tip, and work-origin management.
  • Implement point management: load points.json, save/update points, and save_current_position_as_point using the last commanded work coordinates rather than reading from motors to avoid dependency on direct status reads.
  • Add demo utilities for washing/purging/ejecting pipette tips, moving to named points with z_offset, basic XYZ motion, and helper scripts for slave scanning and printing current XYZ steps.
  • Add default points.json and work_origin.json placeholders to support point/origin persistence used by Station and XYZStepperController.
unilabos/devices/SII/laiyu_xyz_pipette/station.py
unilabos/devices/SII/laiyu_xyz_pipette/demo_pipette.py
unilabos/devices/SII/laiyu_xyz_pipette/demo_points.py
unilabos/devices/SII/laiyu_xyz_pipette/demo_xyz.py
unilabos/devices/SII/laiyu_xyz_pipette/test_save_points_x.py
unilabos/devices/SII/laiyu_xyz_pipette/test_save_points_y.py
unilabos/devices/SII/laiyu_xyz_pipette/test_scan_slaves.py
unilabos/devices/SII/laiyu_xyz_pipette/print_xyz_steps.py
unilabos/devices/SII/laiyu_xyz_pipette/points.json
unilabos/devices/SII/laiyu_xyz_pipette/work_origin.json
Clean up obsolete registry entries for legacy HPLC, Raman, OPC UA example, and iCL42 motor devices.
  • Remove detailed hplc.agilent device definition from characterization_chromatic registry, leaving only the hplc.agilent-zhida entry.
  • Replace raman.home_made and opcua_example registry contents with empty maps in their respective YAMLs, effectively disabling those example devices.
  • Remove motor.iCL42 definition from robot_linear_motion registry, including its UniLabJsonCommand actions and init/data schemas.
unilabos/registry/devices/characterization_chromatic.yaml
unilabos/registry/devices/characterization_optic.yaml
unilabos/registry/devices/opcua_example.yaml
unilabos/registry/devices/robot_linear_motion.yaml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant