python语言全自动光盘驱动器触觉控制机器人软件代码ZQ1
·
import tkinter as tk
from tkinter import scrolledtext, messagebox, filedialog, ttk
import pygame
from datetime import datetime
import pyttsx3
import queue
import time
import platform
import pyautogui
from pynput import keyboard, mouse
import sys
import ctypes
import os
import win32api
import win32file
import threading
import subprocess
from ctypes import wintypes, Structure, POINTER, byref, c_ubyte, c_uint32, c_ushort, c_ulong
# ATA命令结构体
class ATA_PASS_THROUGH_EX(Structure):
_fields_ = [
("Length", c_ushort),
("AtaFlags", c_ushort),
("PathId", c_ubyte),
("TargetId", c_ubyte),
("Lun", c_ubyte),
("ReservedAsUchar", c_ubyte),
("DataTransferLength", c_ulong),
("TimeOutValue", c_ulong),
("ReservedAsUlong", c_ulong),
("DataBufferOffset", c_ulong),
("PreviousTaskFile", c_ubyte * 8),
("CurrentTaskFile", c_ubyte * 8)
]
class CDDriveMotorControl:
def __init__(self, root):
self.root = root
self.root.title("全自动光盘驱动器底层电机控制机器人")
self.root.geometry("1000x900")
self.root.configure(bg="#f0f0f0")
# 检查管理员权限
self.check_admin_privileges()
# 初始化pygame音频系统
pygame.mixer.init()
pygame.mixer.set_num_channels(8)
# TTS 配置
self.tts_method = "system"
self.tts_queue = queue.Queue()
self.tts_running = True
self.tts_thread = threading.Thread(target=self._tts_worker, daemon=True)
self.tts_thread.start()
# 初始化备用TTS
self.backup_tts_engine = None
self.setup_backup_tts()
# 对话历史
self.conversation_history = []
# 光盘驱动器列表
self.cd_drives = []
self.selected_drive = None
# 电机控制参数
self.motor_speed = 50
self.motor_running = False
self.motor_direction = "stop"
# 物理测试模式
self.physical_test_mode = True
self.setup_ui()
self.show_admin_warning()
self.setup_keyboard_listener()
self.scan_cd_drives()
# 启动状态监控线程
self.status_monitor_thread = threading.Thread(target=self._status_monitor, daemon=True)
self.status_monitor_thread.start()
# 初始语音提示
self.speak_response("光盘驱动器底层电机控制机器人已启动")
def scan_cd_drives(self):
"""扫描电脑上的光盘驱动器"""
self.cd_drives = []
if platform.system() == "Windows":
try:
drives = win32api.GetLogicalDriveStrings()
drives = drives.split('\x00')[:-1]
for drive in drives:
drive_type = win32file.GetDriveType(drive)
if drive_type == win32file.DRIVE_CDROM:
drive_info = {
'letter': drive,
'type': 'CD/DVD驱动器',
'status': '就绪'
}
try:
volume_info = win32api.GetVolumeInformation(drive)
drive_info['label'] = volume_info[0] if volume_info[0] else '无标签'
drive_info['filesystem'] = volume_info[1]
except:
drive_info['label'] = '无盘片'
drive_info['filesystem'] = '未知'
self.cd_drives.append(drive_info)
except Exception as e:
self.update_conversation(f"扫描驱动器时出错: {e}")
if not self.cd_drives:
self.cd_drives = [
{'letter': 'D:', 'type': '模拟CD驱动器', 'status': '就绪', 'label': '测试盘片', 'filesystem': 'CDFS'},
{'letter': 'E:', 'type': '模拟DVD驱动器', 'status': '就绪', 'label': '数据光盘', 'filesystem': 'UDF'}
]
self.update_conversation("未找到物理光盘驱动器,使用模拟驱动器进行测试")
self.update_drive_selector()
if self.cd_drives:
self.selected_drive = self.cd_drives[0]['letter']
self.update_conversation(f"找到 {len(self.cd_drives)} 个光盘驱动器")
self.speak_response(f"找到{len(self.cd_drives)}个光盘驱动器")
def open_drive_handle(self, drive_letter):
"""打开驱动器句柄"""
try:
drive_char = drive_letter[0].upper()
device_name = f"\\\\.\\{drive_char}:"
handle = ctypes.windll.kernel32.CreateFileW(
device_name,
0x80000000 | 0x40000000, # GENERIC_READ | GENERIC_WRITE
0x00000001 | 0x00000002, # FILE_SHARE_READ | FILE_SHARE_WRITE
None,
3, # OPEN_EXISTING
0x80, # FILE_ATTRIBUTE_NORMAL
None
)
if handle != -1:
return handle
else:
return None
except:
return None
def send_ata_command(self, drive_letter, command, feature=0, count=0, lba_low=0, lba_mid=0, lba_high=0, device=0, data_buffer=None, data_length=0):
"""发送ATA命令到驱动器"""
handle = self.open_drive_handle(drive_letter)
if not handle:
return False, "无法打开驱动器句柄"
try:
# 准备ATA_PASS_THROUGH_EX结构
apt = ATA_PASS_THROUGH_EX()
apt.Length = ctypes.sizeof(ATA_PASS_THROUGH_EX)
apt.AtaFlags = 0x02 # ATA_FLAGS_DATA_IN
apt.DataTransferLength = data_length
apt.TimeOutValue = 10
apt.DataBufferOffset = ctypes.sizeof(ATA_PASS_THROUGH_EX)
# 设置当前任务文件
apt.CurrentTaskFile[0] = feature # Feature Register
apt.CurrentTaskFile[1] = count # Sector Count
apt.CurrentTaskFile[2] = lba_low # LBA Low
apt.CurrentTaskFile[3] = lba_mid # LBA Mid
apt.CurrentTaskFile[4] = lba_high # LBA High
apt.CurrentTaskFile[5] = device # Device Register
apt.CurrentTaskFile[6] = command # Command Register
# 计算总缓冲区大小
total_size = ctypes.sizeof(ATA_PASS_THROUGH_EX) + data_length
buffer = (ctypes.c_ubyte * total_size)()
# 复制结构体到缓冲区
ctypes.memmove(ctypes.byref(buffer), ctypes.byref(apt), ctypes.sizeof(apt))
# 复制数据缓冲区
if data_buffer and data_length > 0:
data_ptr = ctypes.addressof(buffer) + ctypes.sizeof(apt)
ctypes.memmove(data_ptr, data_buffer, data_length)
bytes_returned = wintypes.DWORD()
success = ctypes.windll.kernel32.DeviceIoControl(
handle,
0x4D02C, # IOCTL_ATA_PASS_THROUGH
buffer,
total_size,
buffer,
total_size,
byref(bytes_returned),
None
)
ctypes.windll.kernel32.CloseHandle(handle)
return bool(success), f"ATA命令执行{'成功' if success else '失败'}"
except Exception as e:
ctypes.windll.kernel32.CloseHandle(handle)
return False, f"ATA命令执行出错: {str(e)}"
def start_spindle_motor_ata(self, drive_letter):
"""使用ATA命令启动主轴电机"""
try:
# ATA命令: START MOTOR (非标准命令,某些驱动器支持)
success, message = self.send_ata_command(drive_letter, 0x94, 0x01) # 0x94 = SPIN UP
if not success:
# 尝试其他ATA命令
success, message = self.send_ata_command(drive_letter, 0xEF, 0x03) # 0xEF = SET FEATURES, 0x03 = Spin-up
return success, message
except Exception as e:
return False, f"ATA启动电机出错: {str(e)}"
def stop_spindle_motor_ata(self, drive_letter):
"""使用ATA命令停止主轴电机"""
try:
# ATA命令: STOP MOTOR
success, message = self.send_ata_command(drive_letter, 0x95, 0x00) # 0x95 = SPIN DOWN
if not success:
# 尝试其他ATA命令
success, message = self.send_ata_command(drive_letter, 0xEF, 0x04) # 0xEF = SET FEATURES, 0x04 = Spin-down
return success, message
except Exception as e:
return False, f"ATA停止电机出错: {str(e)}"
def physical_motor_test(self, drive_letter, direction="cw"):
"""物理电机测试 - 使用多种方法尝试启动电机"""
self.update_conversation("=== 开始物理电机测试 ===")
self.update_conversation(f"目标驱动器: {drive_letter}")
self.update_conversation(f"方向: {direction}")
test_results = []
# 方法1: 使用模拟光盘访问强制电机启动
try:
self.update_conversation("方法1: 尝试模拟光盘读取...")
drive_char = drive_letter[0].upper()
test_file = f"{drive_char}:\\test.dat"
# 尝试读取驱动器(这会强制电机启动)
handle = self.open_drive_handle(drive_letter)
if handle:
buffer = (ctypes.c_ubyte * 512)()
bytes_read = wintypes.DWORD()
success = ctypes.windll.kernel32.ReadFile(
handle,
buffer,
512,
byref(bytes_read),
None
)
ctypes.windll.kernel32.CloseHandle(handle)
test_results.append(("模拟读取", bool(success)))
self.update_conversation(f" - 模拟读取: {'成功' if success else '失败'}")
except Exception as e:
test_results.append(("模拟读取", False))
self.update_conversation(f" - 模拟读取: 失败 ({str(e)})")
# 方法2: 使用设备控制命令
try:
self.update_conversation("方法2: 尝试设备控制命令...")
handle = self.open_drive_handle(drive_letter)
if handle:
# 发送通用设备控制命令
success = ctypes.windll.kernel32.DeviceIoControl(
handle,
0x00070020, # 通用IOCTL
None, 0,
None, 0,
byref(wintypes.DWORD()),
None
)
ctypes.windll.kernel32.CloseHandle(handle)
test_results.append(("设备控制", bool(success)))
self.update_conversation(f" - 设备控制: {'成功' if success else '失败'}")
except Exception as e:
test_results.append(("设备控制", False))
self.update_conversation(f" - 设备控制: 失败 ({str(e)})")
# 方法3: 使用电源管理命令
try:
self.update_conversation("方法3: 尝试电源管理命令...")
handle = self.open_drive_handle(drive_letter)
if handle:
# 发送设备电源命令
success = ctypes.windll.kernel32.DeviceIoControl(
handle,
0x000700E0, # 电源管理IOCTL
None, 0,
None, 0,
byref(wintypes.DWORD()),
None
)
ctypes.windll.kernel32.CloseHandle(handle)
test_results.append(("电源管理", bool(success)))
self.update_conversation(f" - 电源管理: {'成功' if success else '失败'}")
except Exception as e:
test_results.append(("电源管理", False))
self.update_conversation(f" - 电源管理: 失败 ({str(e)})")
# 方法4: 使用物理设备名称访问
try:
self.update_conversation("方法4: 尝试物理设备访问...")
# 获取物理设备路径
physical_path = self.get_physical_device_path(drive_letter)
if physical_path:
self.update_conversation(f" - 物理设备路径: {physical_path}")
test_results.append(("物理设备", True))
else:
test_results.append(("物理设备", False))
self.update_conversation(" - 物理设备: 无法获取路径")
except Exception as e:
test_results.append(("物理设备", False))
self.update_conversation(f" - 物理设备: 失败 ({str(e)})")
# 分析测试结果
success_count = sum(1 for _, success in test_results if success)
total_tests = len(test_results)
self.update_conversation(f"=== 测试完成: {success_count}/{total_tests} 项成功 ===")
if success_count > 0:
return True, f"物理测试完成,{success_count}/{total_tests} 项成功"
else:
return False, "所有物理测试方法均失败"
def get_physical_device_path(self, drive_letter):
"""获取物理设备路径"""
try:
drive_char = drive_letter[0].upper()
result = subprocess.run(
['wmic', 'logicaldisk', 'where', f"DeviceID='{drive_char}:'", 'get', 'DeviceID,VolumeName'],
capture_output=True, text=True
)
return result.stdout if result.returncode == 0 else None
except:
return None
def control_motor_physical(self, drive_letter, direction="cw"):
"""物理电机控制"""
if not self.physical_test_mode:
return False, "物理测试模式未启用"
try:
# 首先尝试ATA命令
if direction in ["cw", "ccw"]:
success, message = self.start_spindle_motor_ata(drive_letter)
if success:
return True, f"ATA命令启动电机{direction}成功"
# 如果ATA失败,进行物理测试
return self.physical_motor_test(drive_letter, direction)
except Exception as e:
return False, f"物理控制出错: {str(e)}"
def update_drive_selector(self):
"""更新驱动器选择器"""
if hasattr(self, 'drive_selector'):
self.drive_selector['values'] = [f"{drive['letter']} - {drive['type']}" for drive in self.cd_drives]
if self.cd_drives:
self.drive_selector.current(0)
self.update_drive_info()
def get_drive_info(self, drive_letter):
"""获取指定驱动器的详细信息"""
for drive in self.cd_drives:
if drive['letter'] == drive_letter:
return drive
return None
def update_drive_info(self):
"""更新驱动器信息显示"""
if self.selected_drive:
drive_info = self.get_drive_info(self.selected_drive)
if drive_info:
info_text = f"驱动器: {drive_info['letter']}\n类型: {drive_info['type']}\n状态: {drive_info['status']}\n标签: {drive_info['label']}"
self.drive_info_var.set(info_text)
def setup_keyboard_listener(self):
"""设置键盘监听器"""
def on_press(key):
try:
if key == keyboard.Key.left:
self.control_motor("ccw")
elif key == keyboard.Key.right:
self.control_motor("cw")
elif key == keyboard.Key.up:
self.close_tray()
elif key == keyboard.Key.down:
self.open_tray()
elif key == keyboard.Key.space:
self.control_motor("stop")
elif hasattr(key, 'char'):
if key.char == '[':
self.speed_down()
elif key.char == ']':
self.speed_up()
elif key.char == 'p':
self.toggle_physical_mode()
except Exception as e:
print(f"键盘监听错误: {e}")
self.keyboard_listener = keyboard.Listener(on_press=on_press)
self.keyboard_listener.daemon = True
self.keyboard_listener.start()
def check_admin_privileges(self):
"""检查管理员权限"""
try:
if platform.system() == "Windows":
self.is_admin = ctypes.windll.shell32.IsUserAnAdmin()
else:
self.is_admin = os.getuid() == 0
except:
self.is_admin = False
def show_admin_warning(self):
"""显示管理员权限警告"""
if not self.is_admin:
self.update_conversation("警告: 程序可能没有管理员权限,某些硬件操作可能无法执行")
self.update_conversation("建议: 以管理员身份重新运行此程序")
def setup_backup_tts(self):
"""初始化备用TTS引擎"""
try:
self.backup_tts_engine = pyttsx3.init()
voices = self.backup_tts_engine.getProperty('voices')
for voice in voices:
if any(keyword in voice.id.lower() for keyword in ['chinese', 'china', 'mandarin', 'zh']):
self.backup_tts_engine.setProperty('voice', voice.id)
break
self.backup_tts_engine.setProperty('rate', 200)
self.backup_tts_engine.setProperty('volume', 0.8)
except Exception as e:
print(f"备用TTS初始化失败: {e}")
def open_tray(self):
"""打开托盘"""
if not self.selected_drive:
self.speak_response("请先选择驱动器")
return False
try:
drive_char = self.selected_drive[0].upper()
device_name = f"\\\\.\\{drive_char}:"
handle = ctypes.windll.kernel32.CreateFileW(
device_name,
0x80000000,
0x00000003,
None,
3,
0,
None
)
if handle != -1:
ctypes.windll.kernel32.DeviceIoControl(
handle,
0x2D4808, # IOCTL_STORAGE_EJECT_MEDIA
None, 0, None, 0, None, None
)
ctypes.windll.kernel32.CloseHandle(handle)
self.update_conversation(f"✓ 驱动器 {self.selected_drive} 托盘已打开")
self.tray_status_var.set("托盘状态: 已打开")
self.speak_response("托盘打开成功")
return True
else:
self.update_conversation(f"✗ 无法打开驱动器 {self.selected_drive} 托盘")
self.speak_response("托盘打开失败")
return False
except Exception as e:
self.update_conversation(f"打开托盘时出错: {str(e)}")
self.speak_response("打开托盘出错")
return False
def close_tray(self):
"""关闭托盘"""
if not self.selected_drive:
self.speak_response("请先选择驱动器")
return False
try:
drive_char = self.selected_drive[0].upper()
device_name = f"\\\\.\\{drive_char}:"
handle = ctypes.windll.kernel32.CreateFileW(
device_name,
0x80000000,
0x00000003,
None,
3,
0,
None
)
if handle != -1:
ctypes.windll.kernel32.DeviceIoControl(
handle,
0x2D480C, # IOCTL_STORAGE_LOAD_MEDIA
None, 0, None, 0, None, None
)
ctypes.windll.kernel32.CloseHandle(handle)
self.update_conversation(f"✓ 驱动器 {self.selected_drive} 托盘关闭命令已发送")
self.tray_status_var.set("托盘状态: 已关闭")
self.speak_response("托盘关闭")
return True
else:
self.update_conversation(f"✗ 无法关闭驱动器 {self.selected_drive} 托盘")
self.speak_response("托盘关闭失败")
return False
except Exception as e:
self.update_conversation(f"关闭托盘时出错: {str(e)}")
self.speak_response("关闭托盘出错")
return False
def control_motor(self, direction):
"""控制电机"""
if not self.selected_drive:
self.speak_response("请先选择驱动器")
return
try:
if direction == "cw":
self.update_conversation(f"正在启动驱动器 {self.selected_drive} 电机顺时针旋转...")
success, message = self.control_motor_physical(self.selected_drive, "cw")
if success:
self.motor_running = True
self.motor_direction = "cw"
self.update_conversation(f"✓ {message}")
self.speak_response(f"电机顺时针旋转,速度{self.motor_speed}%")
self.motor_status_var.set(f"电机状态: 顺时针旋转 ({self.motor_speed}%)")
else:
self.update_conversation(f"✗ {message}")
self.speak_response("启动电机失败")
elif direction == "ccw":
self.update_conversation(f"正在启动驱动器 {self.selected_drive} 电机逆时针旋转...")
success, message = self.control_motor_physical(self.selected_drive, "ccw")
if success:
self.motor_running = True
self.motor_direction = "ccw"
self.update_conversation(f"✓ {message}")
self.speak_response(f"电机逆时针旋转,速度{self.motor_speed}%")
self.motor_status_var.set(f"电机状态: 逆时针旋转 ({self.motor_speed}%)")
else:
self.update_conversation(f"✗ {message}")
self.speak_response("启动电机失败")
elif direction == "stop":
self.update_conversation(f"正在停止驱动器 {self.selected_drive} 电机...")
success, message = self.stop_spindle_motor_ata(self.selected_drive)
if success:
self.motor_running = False
self.motor_direction = "stop"
self.update_conversation("✓ 电机停止命令已发送")
self.speak_response("电机停止")
self.motor_status_var.set("电机状态: 停止")
else:
self.update_conversation(f"✗ 停止电机失败: {message}")
self.speak_response("停止电机失败")
except Exception as e:
self.update_conversation(f"控制电机时出错: {str(e)}")
self.speak_response("控制电机出错")
def speed_up(self):
"""加速"""
self.motor_speed = min(100, self.motor_speed + 10)
if self.motor_running:
self.update_conversation(f"电机速度增加到{self.motor_speed}%")
self.speak_response(f"速度{self.motor_speed}%")
def speed_down(self):
"""减速"""
self.motor_speed = max(10, self.motor_speed - 10)
if self.motor_running:
self.update_conversation(f"电机速度减少到{self.motor_speed}%")
self.speak_response(f"速度{self.motor_speed}%")
def toggle_physical_mode(self):
"""切换物理测试模式"""
self.physical_test_mode = not self.physical_test_mode
mode_text = "启用" if self.physical_test_mode else "禁用"
self.update_conversation(f"物理测试模式: {mode_text}")
self.speak_response(f"物理测试模式{mode_text}")
def setup_ui(self):
# 创建主框架
main_frame = tk.Frame(self.root, bg="#f0f0f0")
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 标题
title_label = tk.Label(main_frame, text="全自动光盘驱动器底层电机控制机器人",
font=("Arial", 16, "bold"), bg="#f0f0f0", fg="#2c3e50")
title_label.pack(pady=10)
# 权限状态显示
admin_status = "管理员权限: 是" if self.is_admin else "管理员权限: 否 (建议以管理员身份运行)"
admin_label = tk.Label(main_frame, text=admin_status,
font=("Arial", 10),
bg="#f0f0f0",
fg="green" if self.is_admin else "red")
admin_label.pack(pady=5)
# 创建左右分栏
content_frame = tk.Frame(main_frame, bg="#f0f0f0")
content_frame.pack(fill=tk.BOTH, expand=True)
# 左侧控制面板
control_frame = tk.Frame(content_frame, bg="#e0e0e0", width=400)
control_frame.pack(side=tk.LEFT, fill=tk.Y, padx=(0, 10))
control_frame.pack_propagate(False)
# 右侧状态显示
status_frame = tk.Frame(content_frame, bg="#f8f9fa")
status_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)
# === 左侧控制面板内容 ===
# 驱动器选择
drive_frame = tk.Frame(control_frame, bg="#e0e0e0", pady=10)
drive_frame.pack(fill=tk.X, padx=10)
tk.Label(drive_frame, text="光盘驱动器选择", font=("Arial", 14, "bold"), bg="#e0e0e0").pack(anchor="w")
# 驱动器选择器
selector_frame = tk.Frame(drive_frame, bg="#e0e0e0")
selector_frame.pack(fill=tk.X, pady=5)
tk.Label(selector_frame, text="选择驱动器:", bg="#e0e0e0").pack(side=tk.LEFT)
self.drive_selector = ttk.Combobox(selector_frame, width=25, state="readonly")
self.drive_selector.pack(side=tk.LEFT, padx=5)
self.drive_selector.bind('<<ComboboxSelected>>', self.on_drive_selected)
# 刷新按钮
refresh_btn = tk.Button(selector_frame, text="刷新", command=self.scan_cd_drives, width=8)
refresh_btn.pack(side=tk.LEFT, padx=5)
# 驱动器信息显示
info_frame = tk.Frame(drive_frame, bg="#e0e0e0")
info_frame.pack(fill=tk.X, pady=5)
self.drive_info_var = tk.StringVar(value="请选择驱动器")
info_label = tk.Label(info_frame, textvariable=self.drive_info_var, bg="#e0e0e0",
wraplength=350, justify="left", font=("Arial", 9))
info_label.pack(anchor="w")
# 物理测试模式
physical_frame = tk.Frame(control_frame, bg="#e0e0e0", pady=10)
physical_frame.pack(fill=tk.X, padx=10)
self.physical_var = tk.BooleanVar(value=True)
physical_btn = tk.Checkbutton(physical_frame, text="启用物理测试模式",
variable=self.physical_var, bg="#e0e0e0",
command=self.toggle_physical_mode)
physical_btn.pack(anchor="w")
# 状态显示
status_control_frame = tk.Frame(control_frame, bg="#e0e0e0", pady=10)
status_control_frame.pack(fill=tk.X, padx=10)
self.motor_status_var = tk.StringVar(value="电机状态: 停止")
tk.Label(status_control_frame, textvariable=self.motor_status_var, bg="#e0e0e0",
font=("Arial", 10, "bold")).pack(anchor="w")
self.tray_status_var = tk.StringVar(value="托盘状态: 未知")
tk.Label(status_control_frame, textvariable=self.tray_status_var, bg="#e0e0e0").pack(anchor="w")
# 电机控制按钮
motor_frame = tk.Frame(control_frame, bg="#e0e0e0", pady=10)
motor_frame.pack(fill=tk.X, padx=10)
tk.Label(motor_frame, text="电机控制", font=("Arial", 12, "bold"), bg="#e0e0e0").pack(anchor="w")
btn_frame1 = tk.Frame(motor_frame, bg="#e0e0e0")
btn_frame1.pack(fill=tk.X, pady=5)
self.cw_btn = tk.Button(btn_frame1, text="顺时针旋转", command=lambda: self.control_motor("cw"),
width=12, height=2, bg="#4CAF50", fg="white", font=("Arial", 10, "bold"))
self.cw_btn.pack(side=tk.LEFT, padx=2)
self.ccw_btn = tk.Button(btn_frame1, text="逆时针旋转", command=lambda: self.control_motor("ccw"),
width=12, height=2, bg="#2196F3", fg="white", font=("Arial", 10, "bold"))
self.ccw_btn.pack(side=tk.LEFT, padx=2)
self.stop_btn = tk.Button(btn_frame1, text="停止电机", command=lambda: self.control_motor("stop"),
width=12, height=2, bg="#f44336", fg="white", font=("Arial", 10, "bold"))
self.stop_btn.pack(side=tk.LEFT, padx=2)
# 托盘控制
tray_frame = tk.Frame(control_frame, bg="#e0e0e0", pady=10)
tray_frame.pack(fill=tk.X, padx=10)
tk.Label(tray_frame, text="托盘控制", font=("Arial", 12, "bold"), bg="#e0e0e0").pack(anchor="w")
btn_frame2 = tk.Frame(tray_frame, bg="#e0e0e0")
btn_frame2.pack(fill=tk.X, pady=5)
self.open_btn = tk.Button(btn_frame2, text="打开托盘", command=self.open_tray,
width=12, bg="#9C27B0", fg="white")
self.open_btn.pack(side=tk.LEFT, padx=2)
self.close_btn = tk.Button(btn_frame2, text="关闭托盘", command=self.close_tray,
width=12, bg="#9C27B0", fg="white")
self.close_btn.pack(side=tk.LEFT, padx=2)
# 系统状态
system_frame = tk.Frame(control_frame, bg="#e0e0e0", pady=10)
system_frame.pack(fill=tk.X, padx=10)
self.status_var = tk.StringVar(value="系统就绪")
tk.Label(system_frame, text="系统状态:", font=("Arial", 11, "bold"), bg="#e0e0e0").pack(anchor="w")
status_display = tk.Label(system_frame, textvariable=self.status_var,
bg="#e0e0e0", fg="blue", wraplength=350, justify="left",
font=("Arial", 10))
status_display.pack(pady=5, anchor="w")
# 键盘控制提示
keyboard_frame = tk.Frame(control_frame, bg="#e0e0e0", pady=10)
keyboard_frame.pack(fill=tk.BOTH, expand=True, padx=10)
tk.Label(keyboard_frame, text="键盘控制说明", font=("Arial", 12, "bold"), bg="#e0e0e0").pack(anchor="w")
keyboard_text = """方向键控制:
• → 右方向键: 电机顺时针旋转
• ← 左方向键: 电机逆时针旋转
• ↑ 上方向键: 关闭托盘
• ↓ 下方向键: 打开托盘
• 空格键: 停止电机
• [ 键: 减速
• ] 键: 加速
• P 键: 切换物理测试模式
技术说明:
• 使用ATA/SCSI底层命令
• 物理测试模式强制电机启动
• 需要管理员权限
• 某些现代光驱可能限制电机控制"""
keyboard_label = tk.Label(keyboard_frame, text=keyboard_text, bg="#e0e0e0",
justify="left", font=("Arial", 9), wraplength=350)
keyboard_label.pack(pady=5, anchor="w")
# === 右侧状态显示内容 ===
# 操作记录区域
conversation_frame = tk.Frame(status_frame, bg="#f8f9fa")
conversation_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
tk.Label(conversation_frame, text="操作记录", font=("Arial", 14, "bold"),
bg="#f8f9fa").pack(anchor="w")
self.conversation_text = scrolledtext.ScrolledText(conversation_frame,
wrap=tk.WORD,
width=60,
height=20,
font=("SimHei", 10),
bg="white",
relief=tk.SUNKEN,
bd=2)
self.conversation_text.pack(fill=tk.BOTH, expand=True, pady=5)
self.conversation_text.config(state=tk.DISABLED)
def on_drive_selected(self, event):
"""驱动器选择事件"""
selection = self.drive_selector.get()
if selection:
drive_letter = selection.split(' - ')[0]
self.selected_drive = drive_letter
self.update_drive_info()
self.update_conversation(f"已选择驱动器 {drive_letter}")
self.speak_response(f"选择驱动器{drive_letter.replace(':', '')}")
def _status_monitor(self):
"""状态监控线程"""
while True:
try:
time.sleep(0.5)
except:
pass
def _tts_worker(self):
"""TTS工作线程"""
while self.tts_running:
try:
text = self.tts_queue.get(timeout=0.5)
if text is None:
break
self.root.after(0, lambda: self.status_var.set("正在语音反馈..."))
success = False
if self.tts_method == "system":
success = self._speak_with_system(text)
elif self.tts_method == "pyttsx3":
success = self._speak_with_pyttsx3(text)
if not success and self.tts_method != "pyttsx3" and self.backup_tts_engine:
self._speak_with_pyttsx3(text)
self.root.after(0, lambda: self.status_var.set("系统就绪"))
self.tts_queue.task_done()
time.sleep(0.05)
except queue.Empty:
continue
except Exception as e:
print(f"TTS工作线程错误: {e}")
def _speak_with_pyttsx3(self, text):
"""使用pyttsx3进行语音合成"""
try:
engine = pyttsx3.init()
engine.setProperty('rate', 220)
engine.setProperty('volume', 0.8)
voices = engine.getProperty('voices')
if voices:
engine.setProperty('voice', voices[0].id)
engine.say(text)
engine.runAndWait()
return True
except Exception as e:
print(f"Pyttsx3 TTS错误: {e}")
return False
def _speak_with_system(self, text):
"""使用系统TTS命令"""
try:
system = platform.system()
short_text = text[:20] if len(text) > 20 else text
if system == "Windows":
os.system(f'powershell -Command "Add-Type -AssemblyName System.Speech; $speak = New-Object System.Speech.Synthesis.SpeechSynthesizer; $speak.Rate = 3; $speak.Speak(\'{short_text}\')"')
return True
elif system == "Darwin":
os.system(f'say -r 300 "{short_text}"')
return True
elif system == "Linux":
os.system(f'espeak -s 300 "{short_text}" 2>/dev/null')
return True
return False
except Exception as e:
print(f"系统TTS错误: {e}")
return False
def speak_response(self, text):
"""语音反馈"""
short_text = text[:20] if len(text) > 20 else text
self.tts_queue.put(short_text)
self.update_conversation("系统: " + text)
self.conversation_history.append(("系统", text, datetime.now()))
def update_conversation(self, text):
"""更新对话显示区域"""
self.conversation_text.config(state=tk.NORMAL)
self.conversation_text.insert(tk.END, text + "\n")
self.conversation_text.see(tk.END)
self.conversation_text.config(state=tk.DISABLED)
def on_closing(self):
"""关闭窗口时的清理工作"""
self.tts_running = False
# 停止所有电机
if self.motor_running and self.selected_drive:
try:
self.stop_spindle_motor_ata(self.selected_drive)
except:
pass
while not self.tts_queue.empty():
try:
self.tts_queue.get_nowait()
self.tts_queue.task_done()
except queue.Empty:
break
pygame.mixer.quit()
self.root.destroy()
if __name__ == "__main__":
# 在Windows上请求管理员权限
if platform.system() == "Windows":
try:
if not ctypes.windll.shell32.IsUserAnAdmin():
ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join(sys.argv), None, 1)
sys.exit()
except:
pass
root = tk.Tk()
app = CDDriveMotorControl(root)
root.protocol("WM_DELETE_WINDOW", app.on_closing)
root.mainloop()
软件截图:

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)