diff --git a/facelightcontrol/CMakeLists2.txt b/facelightcontrol/CMakeLists2.txt new file mode 100644 index 00000000..62e3e6a2 --- /dev/null +++ b/facelightcontrol/CMakeLists2.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 2.14) +project(faceLightClient2) + +set(CMAKE_CXX_FLAGS "$ENV{CXXFLAGS} -O3 -march=native -Wall") +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +include_directories(include) +link_directories(lib) + +add_executable(faceLightClient2 main2.cpp) +target_link_libraries(faceLightClient2 libfaceLight_SDK_arm64.so) + +set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) \ No newline at end of file diff --git a/facelightcontrol/README_faceLight.md b/facelightcontrol/README_faceLight.md new file mode 100644 index 00000000..343dee43 --- /dev/null +++ b/facelightcontrol/README_faceLight.md @@ -0,0 +1,112 @@ +# FaceLight 命令行控制工具 + +## 编译方法 + +```bash +# 使用新的CMakeLists.txt编译 +mkdir build2 +cd build2 +cmake -f ../CMakeLists2.txt .. +make + +# 编译完成后,可执行文件位于 bin/faceLightClient2 +``` + +## 使用方法 + +### 基本语法 +```bash +./bin/faceLightClient2 [选项] +``` + +### 可用选项 + +| 选项 | 长选项 | 参数 | 说明 | +|------|--------|------|------| +| -m | --mode | MODE | 控制模式:all\|single\|cycle\|custom\|pattern | +| -c | --color | COLOR | 颜色:red\|green\|blue\|yellow\|white\|black | +| -i | --index | INDEX | LED索引(0-11),用于single模式 | +| -d | --delay | DELAY | 延时(毫秒),默认2000 | +| -p | --pattern | PATTERN | 自定义图案:"0:red,1:green,2:blue" | +| -t | --times | TIMES | 循环次数,默认无限 | +| -h | --help | | 显示帮助信息 | + +### 控制模式说明 + +#### 1. all - 全部LED同色 +所有LED使用相同颜色 +```bash +./bin/faceLightClient2 -m all -c red # 全部LED红色 +./bin/faceLightClient2 -m all -c blue # 全部LED蓝色 +``` + +#### 2. single - 单个LED控制 +控制指定索引的单个LED +```bash +./bin/faceLightClient2 -m single -i 0 -c red # LED 0 红色 +./bin/faceLightClient2 -m single -i 5 -c green # LED 5 绿色 +``` + +#### 3. cycle - 颜色循环 +自动循环显示不同颜色 +```bash +./bin/faceLightClient2 -m cycle # 无限循环 +./bin/faceLightClient2 -m cycle -t 3 # 循环3次 +./bin/faceLightClient2 -m cycle -d 1000 -t 5 # 每秒切换,循环5次 +``` + +#### 4. custom - RGB交替模式 +原始示例程序的RGB交替模式 +```bash +./bin/faceLightClient2 -m custom +``` + +#### 5. pattern - 自定义图案 +根据指定的图案设置不同LED +```bash +./bin/faceLightClient2 -m pattern -p "0:red,1:green,2:blue" +./bin/faceLightClient2 -m pattern -p "0:red,5:green,10:blue" +``` + +### 使用示例 + +```bash +# 1. 查看帮助 +./bin/faceLightClient2 -h + +# 2. 设置所有LED为红色 +./bin/faceLightClient2 -m all -c red + +# 3. 设置第0个LED为蓝色(其他关闭) +./bin/faceLightClient2 -m single -i 0 -c blue + +# 4. 颜色循环,每1秒切换一次,循环10次 +./bin/faceLightClient2 -m cycle -d 1000 -t 10 + +# 5. 自定义图案:0号红色,3号绿色,6号蓝色 +./bin/faceLightClient2 -m pattern -p "0:red,3:green,6:blue" + +# 6. RGB交替模式 +./bin/faceLightClient2 -m custom +``` + +## LED索引说明 + +机器狗面部LED索引范围:0-11 +- 具体的LED位置映射请参考硬件文档 + +## 可用颜色 + +- red(红色) +- green(绿色) +- blue(蓝色) +- yellow(黄色) +- white(白色) +- black(黑色/关闭) + +## 注意事项 + +1. 使用前确保已正确连接到机器狗系统 +2. 确保libfaceLight_SDK_arm64.so库文件在lib目录中 +3. 程序需要在机器狗系统上运行(ARM64架构) +4. 设置颜色后需要调用sendCmd()函数才能生效(程序已自动处理) \ No newline at end of file diff --git a/facelightcontrol/main2.cpp b/facelightcontrol/main2.cpp new file mode 100644 index 00000000..30ddb1e5 --- /dev/null +++ b/facelightcontrol/main2.cpp @@ -0,0 +1,210 @@ +#include "FaceLightClient.h" +#include +#include +#include +#include +#include +#include + +void printUsage(const char* programName) { + std::cout << "Usage: " << programName << " [OPTIONS]\n"; + std::cout << "\nOptions:\n"; + std::cout << " -m, --mode MODE Control mode: all|single|cycle|custom|pattern\n"; + std::cout << " -c, --color COLOR Color: red|green|blue|yellow|white|black\n"; + std::cout << " -i, --index INDEX LED index (0-11) for single mode\n"; + std::cout << " -d, --delay DELAY Delay in milliseconds (default: 2000)\n"; + std::cout << " -p, --pattern PATTERN Custom pattern: \"0:red,1:green,2:blue\"\n"; + std::cout << " -t, --times TIMES Number of cycles for cycle mode (default: infinite)\n"; + std::cout << " -h, --help Show this help message\n"; + std::cout << "\nExamples:\n"; + std::cout << " " << programName << " -m all -c red # All LEDs red\n"; + std::cout << " " << programName << " -m single -i 0 -c blue # LED 0 blue\n"; + std::cout << " " << programName << " -m cycle -d 1000 -t 5 # Color cycle 5 times\n"; + std::cout << " " << programName << " -m pattern -p \"0:red,5:green\" # Custom pattern\n"; +} + +std::map getColorMap(FaceLightClient& client) { + std::map colors; + colors["red"] = client.red; + colors["green"] = client.green; + colors["blue"] = client.blue; + colors["yellow"] = client.yellow; + colors["white"] = client.white; + colors["black"] = client.black; + return colors; +} + +bool parsePattern(const std::string& pattern, std::vector>& ledPattern) { + std::stringstream ss(pattern); + std::string token; + + while (std::getline(ss, token, ',')) { + size_t colonPos = token.find(':'); + if (colonPos == std::string::npos) { + std::cerr << "Error: Invalid pattern format. Use INDEX:COLOR\n"; + return false; + } + + try { + int index = std::stoi(token.substr(0, colonPos)); + std::string color = token.substr(colonPos + 1); + + if (index < 0 || index > 11) { + std::cerr << "Error: LED index must be 0-11\n"; + return false; + } + + ledPattern.push_back({index, color}); + } catch (const std::exception& e) { + std::cerr << "Error: Invalid pattern format\n"; + return false; + } + } + + return true; +} + +int main(int argc, char* argv[]) { + FaceLightClient client; + auto colors = getColorMap(client); + + // 默认参数 + std::string mode = "all"; + std::string color = "red"; + int ledIndex = 0; + int delay = 2000; + std::string pattern = ""; + int cycles = -1; // -1 表示无限循环 + + // 解析命令行参数 + for (int i = 1; i < argc; i++) { + std::string arg = argv[i]; + + if (arg == "-h" || arg == "--help") { + printUsage(argv[0]); + return 0; + } else if ((arg == "-m" || arg == "--mode") && i + 1 < argc) { + mode = argv[++i]; + } else if ((arg == "-c" || arg == "--color") && i + 1 < argc) { + color = argv[++i]; + } else if ((arg == "-i" || arg == "--index") && i + 1 < argc) { + ledIndex = std::atoi(argv[++i]); + } else if ((arg == "-d" || arg == "--delay") && i + 1 < argc) { + delay = std::atoi(argv[++i]); + } else if ((arg == "-p" || arg == "--pattern") && i + 1 < argc) { + pattern = argv[++i]; + } else if ((arg == "-t" || arg == "--times") && i + 1 < argc) { + cycles = std::atoi(argv[++i]); + } else { + std::cerr << "Error: Unknown argument " << arg << std::endl; + printUsage(argv[0]); + return 1; + } + } + + // 验证参数 + if (colors.find(color) == colors.end()) { + std::cerr << "Error: Invalid color. Available: red, green, blue, yellow, white, black\n"; + return 1; + } + + if (ledIndex < 0 || ledIndex > 11) { + std::cerr << "Error: LED index must be 0-11\n"; + return 1; + } + + if (delay < 0) { + std::cerr << "Error: Delay must be non-negative\n"; + return 1; + } + + std::cout << "FaceLight Controller Starting...\n"; + std::cout << "Mode: " << mode << ", Color: " << color << ", Delay: " << delay << "ms\n"; + + // 执行控制逻辑 + if (mode == "all") { + // 全部LED使用同一颜色 + std::cout << "Setting all LEDs to " << color << std::endl; + client.setAllLed(colors[color]); + client.sendCmd(); + + } else if (mode == "single") { + // 单个LED控制 + std::cout << "Setting LED " << ledIndex << " to " << color << std::endl; + client.setAllLed(client.black); // 先关闭所有LED + client.setLedColor(ledIndex, colors[color]); + client.sendCmd(); + + } else if (mode == "cycle") { + // 颜色循环 + std::cout << "Starting color cycle..." << std::endl; + std::vector cycleColors = {"red", "green", "blue", "yellow", "white"}; + + int count = 0; + while (cycles == -1 || count < cycles) { + for (const auto& cycleColor : cycleColors) { + std::cout << "Cycle " << (count + 1) << ": " << cycleColor << std::endl; + client.setAllLed(colors[cycleColor]); + client.sendCmd(); + usleep(delay * 1000); // 转换为微秒 + } + if (cycles != -1) count++; + } + + } else if (mode == "pattern") { + // 自定义模式 + if (pattern.empty()) { + std::cerr << "Error: Pattern mode requires -p option\n"; + return 1; + } + + std::vector> ledPattern; + if (!parsePattern(pattern, ledPattern)) { + return 1; + } + + std::cout << "Applying custom pattern..." << std::endl; + client.setAllLed(client.black); // 先关闭所有LED + + for (const auto& led : ledPattern) { + int index = led.first; + const std::string& ledColor = led.second; + + if (colors.find(ledColor) == colors.end()) { + std::cerr << "Error: Invalid color in pattern: " << ledColor << std::endl; + return 1; + } + + std::cout << "Setting LED " << index << " to " << ledColor << std::endl; + client.setLedColor(index, colors[ledColor]); + } + client.sendCmd(); + + } else if (mode == "custom") { + // 原始自定义模式(RGB交替) + std::cout << "Applying RGB alternating pattern..." << std::endl; + for (int i = 0; i < 12; ++i) { + switch (i % 3) { + case 0: + client.setLedColor(i, client.red); + break; + case 1: + client.setLedColor(i, client.green); + break; + case 2: + client.setLedColor(i, client.blue); + break; + default: + break; + } + } + client.sendCmd(); + + } else { + std::cerr << "Error: Invalid mode. Available: all, single, cycle, custom, pattern\n"; + return 1; + } + + std::cout << "Command executed successfully!\n"; + return 0; +} \ No newline at end of file diff --git a/src/Client/BattlefieldExplorationSystem b/src/Client/BattlefieldExplorationSystem index 2cd2882c..8e9968cc 100755 Binary files a/src/Client/BattlefieldExplorationSystem and b/src/Client/BattlefieldExplorationSystem differ diff --git a/src/Client/BattlefieldExplorationSystem.pro b/src/Client/BattlefieldExplorationSystem.pro index c0679f57..5fc9a3a2 100644 --- a/src/Client/BattlefieldExplorationSystem.pro +++ b/src/Client/BattlefieldExplorationSystem.pro @@ -12,6 +12,7 @@ INCLUDEPATH += include INCLUDEPATH += include/core INCLUDEPATH += include/ui INCLUDEPATH += AudioModule +INCLUDEPATH += FaceLightModule # Build directories OBJECTS_DIR = build @@ -32,6 +33,7 @@ SOURCES += \ src/ui/components/RightFunctionPanel.cpp \ src/utils/SystemLogger.cpp \ AudioModule/IntelligenceUI.cpp \ + FaceLightModule/FaceLightControl.cpp \ styles/LeftPanelStyleManager.cpp \ styles/ModernStyleManager.cpp @@ -47,6 +49,7 @@ HEADERS += \ include/ui/components/RightFunctionPanel.h \ include/utils/SystemLogger.h \ AudioModule/IntelligenceUI.h \ + FaceLightModule/FaceLightControl.h \ styles/LeftPanelStyleManager.h \ styles/ModernStyleManager.h @@ -54,7 +57,8 @@ HEADERS += \ FORMS += \ forms/main/MainWindow.ui \ forms/dialogs/DeviceDialog.ui \ - AudioModule/IntelligenceUI.ui + AudioModule/IntelligenceUI.ui \ + FaceLightModule/FaceLightControl.ui # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin diff --git a/src/Client/FaceLightModule/FaceLightControl.cpp b/src/Client/FaceLightModule/FaceLightControl.cpp new file mode 100644 index 00000000..5af59617 --- /dev/null +++ b/src/Client/FaceLightModule/FaceLightControl.cpp @@ -0,0 +1,366 @@ +#include "FaceLightControl.h" +#include "ui_FaceLightControl.h" +#include +#include +#include +#include +#include +#include +#include + +FaceLightControl::FaceLightControl(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::FaceLightControl) + , sshProcess(nullptr) + , colorCycleTimer(nullptr) + , currentColorIndex(0) + , isCycling(false) +{ + ui->setupUi(this); + + // 初始化SSH设置 + updateSshSettings(); + + // 初始化颜色循环 + setupColorCycle(); + + // 连接信号槽 + setupUI(); + + // 初始化状态 + updateStatus("FaceLight控制系统已启动,准备就绪"); + ui->progressBar->setValue(0); +} + +FaceLightControl::~FaceLightControl() +{ + if (sshProcess && sshProcess->state() != QProcess::NotRunning) { + sshProcess->kill(); + sshProcess->waitForFinished(3000); + } + + if (colorCycleTimer) { + colorCycleTimer->stop(); + } + + delete ui; +} + +void FaceLightControl::setupUI() +{ + // 连接基础控制按钮信号槽 + connect(ui->setAllLeds, &QPushButton::clicked, this, &FaceLightControl::on_setAllLeds_clicked); + connect(ui->setSingleLed, &QPushButton::clicked, this, &FaceLightControl::on_setSingleLed_clicked); + connect(ui->startColorCycle, &QPushButton::clicked, this, &FaceLightControl::on_startColorCycle_clicked); + connect(ui->stopColorCycle, &QPushButton::clicked, this, &FaceLightControl::on_stopColorCycle_clicked); + connect(ui->applyCustomPattern, &QPushButton::clicked, this, &FaceLightControl::on_applyCustomPattern_clicked); + connect(ui->turnOffAllLeds, &QPushButton::clicked, this, &FaceLightControl::on_turnOffAllLeds_clicked); + + // 连接SSH设置按钮 + connect(ui->saveSshSettings, &QPushButton::clicked, this, &FaceLightControl::on_saveSshSettings_clicked); + connect(ui->testConnection, &QPushButton::clicked, this, &FaceLightControl::on_testConnection_clicked); + + // 连接高级控制按钮 + connect(ui->rgbAlternating, &QPushButton::clicked, this, &FaceLightControl::on_rgbAlternating_clicked); + + // 快速预设按钮连接 - 使用lambda表达式 + connect(ui->preset1, &QPushButton::clicked, this, [this]() { + QString command = buildFaceLightCommand("all", "red"); + executeSSHCommand(command, "预设1: 全部红色"); + }); + + connect(ui->preset2, &QPushButton::clicked, this, [this]() { + QString command = buildFaceLightCommand("all", "blue"); + executeSSHCommand(command, "预设2: 全部蓝色"); + }); + + connect(ui->preset3, &QPushButton::clicked, this, [this]() { + QString command = buildFaceLightCommand("all", "green"); + executeSSHCommand(command, "预设3: 全部绿色"); + }); + + connect(ui->preset4, &QPushButton::clicked, this, [this]() { + QString command = buildFaceLightCommand("all", "white"); + executeSSHCommand(command, "预设4: 全部白色"); + }); +} + +void FaceLightControl::setupColorCycle() +{ + cycleColors << "red" << "green" << "blue" << "yellow" << "white"; + + colorCycleTimer = new QTimer(this); + connect(colorCycleTimer, &QTimer::timeout, this, &FaceLightControl::onColorCycleTimer); +} + +void FaceLightControl::executeSSHCommand(const QString &command, const QString &description) +{ + if (sshProcess && sshProcess->state() != QProcess::NotRunning) { + updateStatus("上一个命令仍在执行中,请稍候...", true); + return; + } + + if (!sshProcess) { + sshProcess = new QProcess(this); + connect(sshProcess, QOverload::of(&QProcess::finished), + this, &FaceLightControl::onSshProcessFinished); + connect(sshProcess, &QProcess::errorOccurred, + this, &FaceLightControl::onSshProcessError); + } + + currentCommand = description; + updateStatus(QString("正在执行: %1").arg(description)); + ui->progressBar->setValue(25); + + // 从UI获取最新的连接设置 + updateSshSettings(); + + // 转义命令中的单引号 + QString escapedCommand = command; + escapedCommand.replace("'", "'\"'\"'"); + + // 构建双跳SSH命令 + QString fullCommand = QString( + "sshpass -p '%1' ssh -T -n -o StrictHostKeyChecking=no -o ConnectTimeout=10 %2@%3 " + "\"sshpass -p '%4' ssh -T -n -o StrictHostKeyChecking=no -o ConnectTimeout=10 %5@%6 'cd ~/Unitree/sdk/faceLightSDK_Nano/build && %7'\"" + ).arg(m_jumpPassword) // 跳板机密码 + .arg(m_jumpUser) // 跳板机用户 + .arg(m_jumpHost) // 跳板机IP + .arg(m_sshPassword) // 目标机密码 + .arg(m_sshUser) // 目标机用户 + .arg(m_sshHost) // 目标机IP + .arg(escapedCommand); // 要执行的命令 + + qDebug() << "执行SSH命令:" << fullCommand; + sshProcess->start("bash", QStringList() << "-c" << fullCommand); +} + +QString FaceLightControl::buildFaceLightCommand(const QString &mode, const QString &color, + int ledIndex, int delay, int times, const QString &pattern) +{ + QString command = "../bin/faceLightClient"; + + // 添加模式参数 + command += QString(" -m %1").arg(mode); + + // 添加颜色参数 + if (!color.isEmpty()) { + command += QString(" -c %1").arg(color); + } + + // 添加LED索引参数 + if (ledIndex >= 0 && ledIndex <= 11) { + command += QString(" -i %1").arg(ledIndex); + } + + // 添加延时参数 + if (delay != 2000) { + command += QString(" -d %1").arg(delay); + } + + // 添加循环次数参数 + if (times != -1) { + command += QString(" -t %1").arg(times); + } + + // 添加自定义图案参数 + if (!pattern.isEmpty()) { + command += QString(" -p \"%1\"").arg(pattern); + } + + return command; +} + +void FaceLightControl::updateSshSettings() +{ + // 目标机器狗设置 + m_sshHost = ui->lineEditTargetIp->text(); + m_sshUser = ui->lineEditTargetUsername->text(); + m_sshPassword = ui->lineEditTargetPassword->text(); + + // 跳板机设置 + m_jumpHost = ui->lineEditJumpIp->text(); + m_jumpUser = ui->lineEditJumpUsername->text(); + m_jumpPassword = ui->lineEditJumpPassword->text(); + + ui->connectionStatusLabel->setText(QString("连接链路: 本机 → %1@%2 → %3@%4") + .arg(m_jumpUser).arg(m_jumpHost) + .arg(m_sshUser).arg(m_sshHost)); +} + +void FaceLightControl::on_setAllLeds_clicked() +{ + QString color = ui->colorComboBox->currentText().toLower(); + QString command = buildFaceLightCommand("all", color); + executeSSHCommand(command, QString("设置所有LED为%1色").arg(color)); +} + +void FaceLightControl::on_setSingleLed_clicked() +{ + QString color = ui->colorComboBox->currentText().toLower(); + int ledIndex = ui->ledIndexSpinBox->value(); + QString command = buildFaceLightCommand("single", color, ledIndex); + executeSSHCommand(command, QString("设置LED %1为%2色").arg(ledIndex).arg(color)); +} + +void FaceLightControl::on_startColorCycle_clicked() +{ + if (isCycling) { + updateStatus("颜色循环已在运行中", true); + return; + } + + int delay = ui->cycleDelaySpinBox->value(); + int times = ui->cycleTimesSpinBox->value(); + + QString command = buildFaceLightCommand("cycle", "", -1, delay, times); + executeSSHCommand(command, QString("启动颜色循环 (延时:%1ms, 次数:%2)").arg(delay).arg(times == -1 ? "无限" : QString::number(times))); +} + +void FaceLightControl::on_stopColorCycle_clicked() +{ + // 发送黑色命令来停止循环 + QString command = buildFaceLightCommand("all", "black"); + executeSSHCommand(command, "停止颜色循环"); + + if (colorCycleTimer->isActive()) { + colorCycleTimer->stop(); + isCycling = false; + updateStatus("本地颜色循环已停止"); + } +} + +void FaceLightControl::on_applyCustomPattern_clicked() +{ + QString pattern = ui->patternLineEdit->text().trimmed(); + if (pattern.isEmpty()) { + updateStatus("错误: 请输入自定义图案", true); + return; + } + + QString command = buildFaceLightCommand("pattern", "", -1, 2000, -1, pattern); + executeSSHCommand(command, QString("应用自定义图案: %1").arg(pattern)); +} + +void FaceLightControl::on_turnOffAllLeds_clicked() +{ + QString command = buildFaceLightCommand("all", "black"); + executeSSHCommand(command, "关闭所有LED"); +} + +void FaceLightControl::on_rgbAlternating_clicked() +{ + QString command = buildFaceLightCommand("custom"); + executeSSHCommand(command, "应用RGB交替模式"); +} + +void FaceLightControl::on_saveSshSettings_clicked() +{ + updateSshSettings(); + updateStatus("SSH连接设置已更新并保存"); +} + +void FaceLightControl::on_testConnection_clicked() +{ + QString command = "echo 'FaceLight连接测试成功'"; + executeSSHCommand(command, "测试SSH连接"); +} + +void FaceLightControl::onColorCycleTimer() +{ + if (!isCycling || cycleColors.isEmpty()) { + return; + } + + QString color = cycleColors[currentColorIndex]; + QString command = buildFaceLightCommand("all", color); + executeSSHCommand(command, QString("循环显示: %1").arg(color)); + + currentColorIndex = (currentColorIndex + 1) % cycleColors.size(); +} + +void FaceLightControl::onSshProcessFinished(int exitCode, QProcess::ExitStatus exitStatus) +{ + ui->progressBar->setValue(100); + + if (exitStatus == QProcess::NormalExit && exitCode == 0) { + updateStatus(QString("%1 - 执行成功").arg(currentCommand)); + } else { + updateStatus(QString("%1 - 执行失败 (退出码: %2)").arg(currentCommand).arg(exitCode), true); + } + + // 读取命令输出 + if (sshProcess) { + QByteArray output = sshProcess->readAllStandardOutput(); + QByteArray error = sshProcess->readAllStandardError(); + + if (!output.isEmpty()) { + updateStatus(QString("输出: %1").arg(QString::fromUtf8(output).trimmed())); + } + + if (!error.isEmpty()) { + updateStatus(QString("错误: %1").arg(QString::fromUtf8(error).trimmed()), true); + } + } + + // 清理进程 + if (sshProcess) { + sshProcess->kill(); + sshProcess->waitForFinished(1000); + sshProcess->deleteLater(); + sshProcess = nullptr; + } + + // 重置进度条 + QTimer::singleShot(2000, [this]() { + ui->progressBar->setValue(0); + }); +} + +void FaceLightControl::onSshProcessError(QProcess::ProcessError error) +{ + ui->progressBar->setValue(0); + + QString errorString; + switch (error) { + case QProcess::FailedToStart: + errorString = "命令启动失败"; + break; + case QProcess::Crashed: + errorString = "命令执行崩溃"; + break; + case QProcess::Timedout: + errorString = "命令执行超时"; + break; + default: + errorString = "未知错误"; + break; + } + + updateStatus(QString("%1 - %2").arg(currentCommand).arg(errorString), true); +} + +void FaceLightControl::updateStatus(const QString &message, bool isError) +{ + QString timestamp = QDateTime::currentDateTime().toString("hh:mm:ss"); + QString logMessage = QString("[%1] %2").arg(timestamp).arg(message); + + if (isError) { + ui->logTextEdit->setTextColor(QColor(255, 100, 100)); + } else { + ui->logTextEdit->setTextColor(QColor(100, 255, 100)); + } + + ui->logTextEdit->append(logMessage); + ui->logTextEdit->setTextColor(QColor(220, 230, 240)); // 重置颜色 + + // 自动滚动到底部 + ui->logTextEdit->moveCursor(QTextCursor::End); +} + +void FaceLightControl::on_quickPresets_clicked() +{ + // 该槽为占位实现,防止链接错误。 + // 如果需要统一触发多个预设,可在此调用相应的命令序列。 + updateStatus("快速预设触发(占位)"); +} \ No newline at end of file diff --git a/src/Client/FaceLightModule/FaceLightControl.h b/src/Client/FaceLightModule/FaceLightControl.h new file mode 100644 index 00000000..1b4e2648 --- /dev/null +++ b/src/Client/FaceLightModule/FaceLightControl.h @@ -0,0 +1,94 @@ +#ifndef FACELIGHTCONTROL_H +#define FACELIGHTCONTROL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE +namespace Ui { +class FaceLightControl; +} +QT_END_NAMESPACE + +class FaceLightControl : public QMainWindow +{ + Q_OBJECT + +public: + FaceLightControl(QWidget *parent = nullptr); + ~FaceLightControl(); + +private slots: + // 基础控制按钮 + void on_setAllLeds_clicked(); + void on_setSingleLed_clicked(); + void on_startColorCycle_clicked(); + void on_stopColorCycle_clicked(); + void on_applyCustomPattern_clicked(); + void on_turnOffAllLeds_clicked(); + + // SSH连接设置 + void on_saveSshSettings_clicked(); + void on_testConnection_clicked(); + + // 高级控制 + void on_rgbAlternating_clicked(); + void on_quickPresets_clicked(); + + // SSH进程处理 + void onSshProcessFinished(int exitCode, QProcess::ExitStatus exitStatus); + void onSshProcessError(QProcess::ProcessError error); + + // 颜色循环定时器 + void onColorCycleTimer(); + +private: + Ui::FaceLightControl *ui; + QProcess *sshProcess; + QString currentCommand; + + // SSH连接信息 + QString m_sshHost; + QString m_sshUser; + QString m_sshPassword; + QString m_jumpHost; + QString m_jumpUser; + QString m_jumpPassword; + + // 颜色循环控制 + QTimer *colorCycleTimer; + QStringList cycleColors; + int currentColorIndex; + bool isCycling; + + // 核心方法 + void executeSSHCommand(const QString &command, const QString &description); + void updateSshSettings(); + + // UI设置和状态更新 + void setupUI(); + void updateStatus(const QString &message, bool isError = false); + void setupColorCycle(); + + // 命令构建方法 + QString buildFaceLightCommand(const QString &mode, const QString &color = "", + int ledIndex = -1, int delay = 2000, + int times = -1, const QString &pattern = ""); +}; + +#endif // FACELIGHTCONTROL_H \ No newline at end of file diff --git a/src/Client/FaceLightModule/FaceLightControl.ui b/src/Client/FaceLightModule/FaceLightControl.ui new file mode 100644 index 00000000..7b27f486 --- /dev/null +++ b/src/Client/FaceLightModule/FaceLightControl.ui @@ -0,0 +1,702 @@ + + + FaceLightControl + + + + 0 + 0 + 1000 + 900 + + + + 机器狗面部灯光控制系统 + + + QMainWindow { + background-color: rgb(24, 33, 45); +} + +QPushButton { + background-color: rgb(30, 44, 62); + color: rgb(220, 230, 240); + border: 2px solid rgba(0, 168, 255, 0.5); + border-radius: 8px; + padding: 12px 20px; + font-size: 14px; + font-weight: bold; + min-height: 35px; +} + +QPushButton:hover { + background-color: rgb(50, 70, 95); + border: 2px solid rgba(0, 168, 255, 0.8); +} + +QPushButton:pressed { + background-color: rgb(40, 60, 85); + border: 2px solid rgba(0, 168, 255, 1.0); +} + +QLabel { + color: rgb(220, 230, 240); + font-size: 14px; +} + +QComboBox, QSpinBox, QLineEdit { + background-color: rgb(30, 44, 62); + color: rgb(220, 230, 240); + border: 2px solid rgba(0, 168, 255, 0.5); + border-radius: 5px; + padding: 8px; + font-size: 14px; +} + +QTextEdit { + background-color: rgb(15, 22, 32); + color: rgb(220, 230, 240); + border: 2px solid rgba(0, 168, 255, 0.3); + border-radius: 5px; + font-family: "Courier New", monospace; + font-size: 12px; +} + +QProgressBar { + border: 2px solid rgba(0, 168, 255, 0.5); + border-radius: 5px; + text-align: center; + background-color: rgb(30, 44, 62); + color: rgb(220, 230, 240); +} + +QProgressBar::chunk { + background-color: rgba(0, 168, 255, 0.8); + border-radius: 3px; +} + +QGroupBox { + font-size: 16px; + font-weight: bold; + color: rgb(0, 168, 255); + border: 2px solid rgba(0, 168, 255, 0.4); + border-radius: 10px; + margin-top: 15px; + padding-top: 10px; +} + +QGroupBox::title { + subcontrol-origin: margin; + subcontrol-position: top center; + padding: 0 15px; + background-color: rgb(24, 33, 45); +} + + + + + 20 + + + 30 + + + 20 + + + 30 + + + 20 + + + + + 💡 机器狗面部灯光控制系统 + + + Qt::AlignCenter + + + QLabel { + color: rgb(0, 168, 255); + font-size: 32px; + font-weight: bold; + padding: 20px; + background: qlineargradient(x1:0, y1:0, x2:1, y2:1, + stop:0 rgba(0, 168, 255, 0.1), + stop:1 rgba(0, 120, 180, 0.1)); + border: 2px solid rgba(0, 168, 255, 0.3); + border-radius: 15px; +} + + + + + + + + 🔗 SSH连接设置 + + + + + + + + 跳板机设置 + + + QGroupBox { font-size: 14px; color: rgb(180, 190, 200); } + + + + + + 跳板机IP: + + + + + + + 192.168.12.1 + + + + + + + 用户名: + + + + + + + pi + + + + + + + 密码: + + + + + + + 123 + + + QLineEdit::Password + + + + + + + + + + + 目标机器狗设置 + + + QGroupBox { font-size: 14px; color: rgb(180, 190, 200); } + + + + + + 机器狗IP: + + + + + + + 192.168.123.13 + + + + + + + 用户名: + + + + + + + unitree + + + + + + + 密码: + + + + + + + 123 + + + QLineEdit::Password + + + + + + + + + + + + + + + 💾 保存设置 + + + + + + + 🔍 测试连接 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + 连接状态: 等待设置... + + + color: rgb(160, 170, 180); font-size: 12px; + + + + + + + + + + + + + 🎨 基础控制 + + + + + + + + 颜色选择: + + + + + + + + Red + + + + + Green + + + + + Blue + + + + + Yellow + + + + + White + + + + + Black + + + + + + + + LED索引: + + + + + + + 11 + + + + + + + + + + 💡 设置所有LED + + + QPushButton { + background-color: rgb(45, 125, 65); + font-size: 16px; + font-weight: bold; +} +QPushButton:hover { background-color: rgb(65, 145, 85); } +QPushButton:pressed { background-color: rgb(55, 135, 75); } + + + + + + + + 🔆 设置单个LED + + + QPushButton { + background-color: rgb(85, 125, 165); + font-size: 16px; + font-weight: bold; +} +QPushButton:hover { background-color: rgb(105, 145, 185); } +QPushButton:pressed { background-color: rgb(95, 135, 175); } + + + + + + + + 🌑 关闭所有LED + + + QPushButton { + background-color: rgb(120, 60, 60); + font-size: 16px; + font-weight: bold; +} +QPushButton:hover { background-color: rgb(140, 80, 80); } +QPushButton:pressed { background-color: rgb(130, 70, 70); } + + + + + + + + + + + ⚡ 高级控制 + + + + + + 🔄 颜色循环设置: + + + font-weight: bold; color: rgb(0, 168, 255); + + + + + + + + + + 延时(ms): + + + + + + + 100 + + + 10000 + + + 1000 + + + ms + + + + + + + 循环次数: + + + + + + + -1 + + + 100 + + + 5 + + + 无限循环 + + + + + + + + + + + + ▶️ 开始循环 + + + QPushButton { + background-color: rgb(45, 125, 65); +} +QPushButton:hover { background-color: rgb(65, 145, 85); } + + + + + + + ⏹️ 停止循环 + + + QPushButton { + background-color: rgb(165, 85, 45); +} +QPushButton:hover { background-color: rgb(185, 105, 65); } + + + + + + + + + + 🎭 自定义图案: + + + font-weight: bold; color: rgb(0, 168, 255); + + + + + + + + 例如: 0:red,3:green,6:blue + + + + + + + + 🎨 应用自定义图案 + + + + + + + + 🌈 RGB交替模式 + + + + + + + + + + + ⚡ 快速预设 + + + + + + 🔴 预设1: 红色 + + + QPushButton { + background-color: rgb(180, 60, 60); +} +QPushButton:hover { background-color: rgb(200, 80, 80); } + + + + + + + + 🔵 预设2: 蓝色 + + + QPushButton { + background-color: rgb(60, 60, 180); +} +QPushButton:hover { background-color: rgb(80, 80, 200); } + + + + + + + + 🟢 预设3: 绿色 + + + QPushButton { + background-color: rgb(60, 180, 60); +} +QPushButton:hover { background-color: rgb(80, 200, 80); } + + + + + + + + ⚪ 预设4: 白色 + + + QPushButton { + background-color: rgb(140, 140, 140); + color: rgb(30, 30, 30); +} +QPushButton:hover { background-color: rgb(160, 160, 160); } + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + 0 + + + true + + + + + + + + 📋 执行日志: + + + font-size: 16px; font-weight: bold; color: rgb(0, 168, 255); + + + + + + + + + 0 + 200 + + + + true + + + + + + + + + + 0 + 0 + 1000 + 22 + + + + + + + + + \ No newline at end of file diff --git a/src/Client/include/ui/components/RightFunctionPanel.h b/src/Client/include/ui/components/RightFunctionPanel.h index 884f24b9..195f61ba 100644 --- a/src/Client/include/ui/components/RightFunctionPanel.h +++ b/src/Client/include/ui/components/RightFunctionPanel.h @@ -183,6 +183,11 @@ signals: */ void openIntelligenceUI(); + /** + * @brief 打开面部灯光控制界面信号 + */ + void openFaceLightUI(); + // 敌情统计模块信号 /** * @brief 刷新敌情统计信号 @@ -247,6 +252,11 @@ private slots: */ void onOpenIntelligenceUI(); + /** + * @brief 打开面部灯光控制界面槽函数 + */ + void onOpenFaceLightUI(); + /** * @brief 刷新统计槽函数 */ @@ -299,6 +309,7 @@ private: // 情报传输模块 ModuleCard *m_intelligenceCard; ///< 情报模块卡片 QPushButton *m_voiceCallBtn; ///< 音频控制按钮 + QPushButton *m_faceLightBtn; ///< 面部灯光控制按钮 // 敌情统计模块 ModuleCard *m_statsCard; ///< 统计模块卡片 diff --git a/src/Client/include/ui/main/MainWindow.h b/src/Client/include/ui/main/MainWindow.h index 05969b0f..0f97612d 100644 --- a/src/Client/include/ui/main/MainWindow.h +++ b/src/Client/include/ui/main/MainWindow.h @@ -40,6 +40,7 @@ // 自定义模块头文件 #include "AudioModule/IntelligenceUI.h" +#include "FaceLightModule/FaceLightControl.h" #include "ui/components/DeviceListPanel.h" #include "ui/components/SystemLogPanel.h" #include "ui/components/RightFunctionPanel.h" @@ -192,6 +193,11 @@ private slots: */ void onIntelligenceClicked(); + /** + * @brief FaceLight控制按钮点击槽函数 + */ + void onFaceLightClicked(); + /** * @brief 设备选中槽函数 * @param deviceId 设备ID @@ -335,6 +341,7 @@ private: private: Ui::MainWindow *m_ui; ///< UI界面指针 IntelligenceUI *m_intelligenceUI; ///< 情报传达界面指针 + FaceLightControl *m_faceLightControl; ///< 面部灯光控制界面指针 DeviceListPanel *m_deviceListPanel; ///< 设备列表面板组件 SystemLogPanel *m_systemLogPanel; ///< 系统日志面板组件 RightFunctionPanel *m_rightFunctionPanel; ///< 右侧功能面板组件 diff --git a/src/Client/src/ui/components/RightFunctionPanel.cpp b/src/Client/src/ui/components/RightFunctionPanel.cpp index cc5d7820..bed89feb 100644 --- a/src/Client/src/ui/components/RightFunctionPanel.cpp +++ b/src/Client/src/ui/components/RightFunctionPanel.cpp @@ -278,14 +278,20 @@ void RightFunctionPanel::setupIntelligenceModule() m_intelligenceCard->setProperty("data-module", "intelligence"); // 情报传达说明 - QLabel *descLabel = new QLabel("🎯 远程音频控制系统"); + QLabel *descLabel = new QLabel("🎯 远程控制系统"); descLabel->setObjectName("intelligence-description"); descLabel->setAlignment(Qt::AlignCenter); descLabel->setStyleSheet("color: #00a8ff; font-size: 14px; font-weight: bold; padding: 8px;"); m_intelligenceCard->addContent(descLabel); - // 主功能按钮 - 打开音频控制界面 - m_voiceCallBtn = new QPushButton("🔊 打开音频控制界面"); + // 按钮布局容器 + QWidget *buttonWidget = new QWidget(); + QVBoxLayout *buttonLayout = new QVBoxLayout(buttonWidget); + buttonLayout->setSpacing(12); + buttonLayout->setContentsMargins(0, 0, 0, 0); + + // 音频控制按钮 + m_voiceCallBtn = new QPushButton("🔊 音频控制模块"); m_voiceCallBtn->setObjectName("FunctionBtn"); m_voiceCallBtn->setProperty("class", "primary-large"); m_voiceCallBtn->setMinimumHeight(55); @@ -310,11 +316,41 @@ void RightFunctionPanel::setupIntelligenceModule() "}" ); + // 面部灯光控制按钮 + m_faceLightBtn = new QPushButton("💡 灯光控制模块"); + m_faceLightBtn->setObjectName("FunctionBtn"); + m_faceLightBtn->setProperty("class", "primary-large"); + m_faceLightBtn->setMinimumHeight(55); + m_faceLightBtn->setStyleSheet( + "QPushButton {" + " background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0," + " stop:0 #ffa502, stop:1 #ff6348);" + " color: white;" + " font-size: 16px;" + " font-weight: bold;" + " border: 2px solid #ffa502;" + " border-radius: 8px;" + " padding: 12px;" + "}" + "QPushButton:hover {" + " background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0," + " stop:0 #ff6348, stop:1 #e55039);" + "}" + "QPushButton:pressed {" + " background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0," + " stop:0 #e55039, stop:1 #c44569);" + "}" + ); + connect(m_voiceCallBtn, &QPushButton::clicked, this, &RightFunctionPanel::onOpenIntelligenceUI); - m_intelligenceCard->addContent(m_voiceCallBtn); + connect(m_faceLightBtn, &QPushButton::clicked, this, &RightFunctionPanel::onOpenFaceLightUI); + + buttonLayout->addWidget(m_voiceCallBtn); + buttonLayout->addWidget(m_faceLightBtn); + m_intelligenceCard->addContent(buttonWidget); // 功能介绍 - QLabel *featureLabel = new QLabel("• SSH双跳连接\n• 音频文件播放\n• 实时录音制作\n• TTS语音合成"); + QLabel *featureLabel = new QLabel("• SSH双跳连接\n• 音频播放控制\n• 面部灯光控制\n• 实时状态监控"); featureLabel->setObjectName("feature-list"); featureLabel->setAlignment(Qt::AlignLeft); featureLabel->setStyleSheet("color: #b0b0b0; font-size: 12px; padding: 10px; line-height: 1.4;"); @@ -846,6 +882,11 @@ void RightFunctionPanel::onOpenIntelligenceUI() emit openIntelligenceUI(); } +void RightFunctionPanel::onOpenFaceLightUI() +{ + emit openFaceLightUI(); +} + void RightFunctionPanel::onRefreshStats() { emit refreshEnemyStats(); diff --git a/src/Client/src/ui/main/MainWindow.cpp b/src/Client/src/ui/main/MainWindow.cpp index 245788d1..729eb29c 100644 --- a/src/Client/src/ui/main/MainWindow.cpp +++ b/src/Client/src/ui/main/MainWindow.cpp @@ -54,6 +54,7 @@ MainWindow::MainWindow(QWidget *parent) , m_rightFunctionPanel(nullptr) , m_leftPanelSplitter(nullptr) , m_intelligenceUI(nullptr) + , m_faceLightControl(nullptr) { m_ui->setupUi(this); @@ -75,6 +76,10 @@ MainWindow::~MainWindow() delete m_intelligenceUI; m_intelligenceUI = nullptr; } + if (m_faceLightControl) { + delete m_faceLightControl; + m_faceLightControl = nullptr; + } delete m_ui; } @@ -230,6 +235,8 @@ void MainWindow::setupRightFunctionPanel() // 情报传输模块信号 connect(m_rightFunctionPanel, &RightFunctionPanel::openIntelligenceUI, this, &MainWindow::onIntelligenceClicked); + connect(m_rightFunctionPanel, &RightFunctionPanel::openFaceLightUI, + this, &MainWindow::onFaceLightClicked); // 敌情统计模块信号 connect(m_rightFunctionPanel, &RightFunctionPanel::refreshEnemyStats, @@ -905,6 +912,16 @@ void MainWindow::onIntelligenceClicked() m_intelligenceUI->raise(); } +void MainWindow::onFaceLightClicked() +{ + if (!m_faceLightControl) { + m_faceLightControl = new FaceLightControl(this); + } + m_faceLightControl->show(); + m_faceLightControl->activateWindow(); + m_faceLightControl->raise(); +} + void MainWindow::onDeviceSelected(const QString &deviceId) { qDebug() << "Device selected:" << deviceId;