diff --git a/App/Config/Config.cpp b/App/Config/Config.cpp index 0b2fd63..34ba46f 100644 --- a/App/Config/Config.cpp +++ b/App/Config/Config.cpp @@ -1,4 +1,4 @@ -#include "Config.h" +#include "Config.h" Config::Config(QObject *parent) : @@ -361,7 +361,45 @@ void Config::set_ExportInfo(QVariant state, QVariant flag, QHostAddress addr, QV } - +void Config::get_Payload_RA_Info(QHostAddress *addr, QVariant *out_port, QVariant *in_port) +{ + QJsonObject obj = getJsonObject("PayloadRAInfo"); + + *addr = obj.value("addr").toString(); + *out_port = obj.value("out_port").toInt(); + *in_port = obj.value("in_port").toInt(); +} + +void Config::set_Payload_RA_Info( QHostAddress addr, QVariant out_port, QVariant in_port) +{ + QJsonObject obj; + + obj["addr"] = addr.toString(); + obj["out_port"] = out_port.toInt(); + obj["in_port"] = in_port.toInt(); + + setJsonObject("PayloadRAInfo",obj); +} + +void Config::get_Payload_UV_Info(QHostAddress *addr, QVariant *out_port, QVariant *in_port) +{ + QJsonObject obj = getJsonObject("PayloadUVInfo"); + + *addr = obj.value("addr").toString(); + *out_port = obj.value("out_port").toInt(); + *in_port = obj.value("in_port").toInt(); +} + +void Config::set_Payload_UV_Info( QHostAddress addr, QVariant out_port, QVariant in_port) +{ + QJsonObject obj; + + obj["addr"] = addr.toString(); + obj["out_port"] = out_port.toInt(); + obj["in_port"] = in_port.toInt(); + + setJsonObject("PayloadUVInfo",obj); +} diff --git a/App/Config/Config.h b/App/Config/Config.h index 84c8cdc..44886e6 100644 --- a/App/Config/Config.h +++ b/App/Config/Config.h @@ -1,4 +1,4 @@ -#ifndef CONFIG_H +#ifndef CONFIG_H #define CONFIG_H #include @@ -72,6 +72,13 @@ public slots: void setReferencePoint(double lon, double lat, double hight); void getReferencePoint(double &lon, double &lat, double &hight); + + void get_Payload_RA_Info(QHostAddress *addr, QVariant *out_port, QVariant *in_port); + void set_Payload_RA_Info( QHostAddress addr, QVariant out_port, QVariant in_port); + + void get_Payload_UV_Info(QHostAddress *addr, QVariant *out_port, QVariant *in_port); + void set_Payload_UV_Info( QHostAddress addr, QVariant out_port, QVariant in_port); + private slots: void loadJson(const QString& jsonFilename); diff --git a/App/GCS_zh_CN.qm b/App/GCS_zh_CN.qm index 3476c01..d98c6e6 100644 Binary files a/App/GCS_zh_CN.qm and b/App/GCS_zh_CN.qm differ diff --git a/App/GCS_zh_CN.ts b/App/GCS_zh_CN.ts index 7231f84..f22efed 100644 --- a/App/GCS_zh_CN.ts +++ b/App/GCS_zh_CN.ts @@ -1944,6 +1944,21 @@ 方向舵 + + + 雷达告警 + + + + + 紫外告警 + + + + + 干扰弹投放 + + 停车 @@ -2227,21 +2242,25 @@ + DataLink 数据链 + RTK 差分基站 + Emergecy - 紧急按钮 + 紧急按钮 + Remote - 遥控器 + 遥控器 @@ -2297,36 +2316,36 @@ 参数 - - + + Communication Lost 通讯丢失 - - + + Communication Regain 通讯已恢复 - + R:%1 / T:%2 收:%1/发:%2 - - - + + + 未定位 - + %1D[%2颗] %1D[%2颗] - + fix[%1颗] %1D Fix 差分[%1颗] @@ -2344,198 +2363,198 @@ GPS错误 - + float[%1颗] 浮点[%1颗] - + err[%1颗] 错误[%1颗] - + ARM 解锁 - + DISARM 上锁 - + MANUAL 手动 - + ALTCTL 定高 - + POSCTL 定点 - + AUTO 自主 - + ACRO - + OFFBOARD - + STABILIZED 增稳 - + RATTITUDE 速率 - + STANDBY 待机 - + BIT 测试 - + AUTO_READY 就绪 - + AUTO_TAKEOFF 起飞 - + AUTO_LOITER 盘旋 - + AUTO_MISSION 任务 - + AUTO_RTL 返航 - + AUTO_LAND 着陆 - + AUTO_RTGS RTGS - + AUTO_FOLLOW_TARGET 目标跟随 - + 开加力 - + 半加力 - + 全加力 - - + + 关加力 - + 连接INS - + INS解算正常 - + INS星数不足 - + INS内部错误 - + INS速度超限 - + SBG解算正常 - + SBG星数不足 - + SBG内部错误 - + SBG速度超限 - + 安控区内 - + 伞舱门开 - + 伞舱门闭 - + 安控区外 @@ -2544,60 +2563,60 @@ 航线切换,当前航线为 %1 - + VERT_OFF OFF 纵向制导关闭 - + VNAV2THT 消除纵向偏差 - + HDOT2THT 控制升降速率 - + GAMMA2THT 控制航迹角 - + AS2THT 俯仰控制速度 - + H2THT 控制绝对高度 - + AGL2THT 控制相对高度 - + LAT_OFF 横向制导关闭 - + LNAV2PHI 滚转消除侧偏 - + PSI2PHI 滚转控制航向 - - - + + + @@ -2606,156 +2625,156 @@ 未定位 - - + + 未初始化 - - + + 垂直陀螺 - - + + AHRS - - + + 速度导航 - - + + 位置导航 - - - - - - - - + + + + + + + + 正常 - - - - - - - - - - + + + + + + + + + + 无效 - - + + 解算正常 - - + + 卫星数不足 - - + + 内部错误 - - + + 速度超限 - - - - + + + + 未知 - - + + 多普勒 - - - - 微分 - - - - - - 单点 - - + 微分 + + + + + + 单点 + + + + + 伪距差分 - - + + SBAS广域差分 - - + + 广域差分 - - + + RTK_FLOAT - - + + RTK_INT - - + + PPP_FLOAT - - + + PPP_INT - - + + FIXED @@ -2764,12 +2783,12 @@ 未支持 - + flight mode 飞行模式 - + 连接SBG @@ -3353,6 +3372,388 @@ Circle(radius) 选择参数... + + Payload + + + Form + + + + + 载荷供电指令 + + + + + 500瓦打开 + + + + + 500瓦电源 + + + + + 500瓦关闭 + + + + + 雷达告警设备 + + + + + + 数据转发IP地址 + + + + + + 数据转发端口 + + + + + + 输出开启 + + + + + + 输出关闭 + + + + + 威胁等级设置 + + + + + 上传威胁等级 + + + + + 注入雷达告警信号 + + + + + + 自动逃逸机动开启 + + + + + + 自动逃逸机动关闭 + + + + + + 受到威胁 + + + + + 紫外告警设备 + + + + + 上电 + + + + + 下电 + + + + + 注入紫外告警信号 + + + + + 干扰弹 + + + + + 自检 + + + + + 保险开 + + + + + 保险关 + + + + + 状态信息 + + + + + 待发指令 + + + + + 发送 + + + + + 取消 + + + + + 雷达控制指令-开始 + + + + + 雷达控制指令-停止 + + + + + + 强制开始机动 + + + + + + 强制退出机动 + + + + + 取消注入雷达告警信号 + + + + + 取消注入紫外告警信号 + + + + + 设备ID + + + + + 工作状态 + + + + + 投放开关 + + + + + 电源电压 + + + + + 火工开关 + + + + + + 干扰弹1 + + + + + 通道1 + + + + + + 干扰弹4 + + + + + 通道4 + + + + + + 干扰弹2 + + + + + 通道2 + + + + + + 干扰弹5 + + + + + 通道5 + + + + + + 干扰弹3 + + + + + 通道3 + + + + + + 干扰弹6 + + + + + 通道6 + + + + + + 左机翼 + + + + + + 右机翼 + + + + + 自检状态 + + + + + 自动释放 + + + + + 自动释放时间间隔(ms) + + + + + + 设置 + + + + + 告警后自动释放-开 + + + + + 告警后自动释放-关 + + + + + + 手动释放 + + + + + 手动释放时间间隔(ms) + + + + + Payload + 任务载荷 + + + + please set command first ,command :%1 + 请设置指令,当前指令号为 %1 + + + + 发送指令 + + + + + %1,%2 accepted and executed + %1,%2 发送成功 + + + + %1,%2 rejected + %1,%2 拒绝执行 + + + + %1,%2 permanently denied + %1,%2 永久拒绝 + + + + %1,%2 unsupported + %1,%2 不支持 + + + + %1,%2 failed + %1,%2 发送失败 + + + + %1,%2 being executed + %1,%2 正在执行 + + + + %1,%2 time out + %1,%2 发送超时 + + + + Please do not operate too fast, the last instruction has not been sent + 请不要操作过快,上一个指令未发送完成 + + PowerSystem @@ -4930,6 +5331,11 @@ Circle(radius) 目标值 + + + 当前飞机: + + 舵面状态 @@ -5045,27 +5451,32 @@ Circle(radius) 工具界面 - + ServoSystem 指令伺服 - + Log 日志 - + + Payload + 任务载荷 + + + Replay 回放 - + PowerSystem 动力系统 - + Senser 传感器 @@ -5076,7 +5487,7 @@ Circle(radius) 弹出右侧窗口-> - + Inspector 监视器 @@ -5089,7 +5500,7 @@ Circle(radius) 指令界面 - + Console 控制台 diff --git a/App/HealthUI/HealthUI.ui b/App/HealthUI/HealthUI.ui index affbc57..c25a2d0 100644 --- a/App/HealthUI/HealthUI.ui +++ b/App/HealthUI/HealthUI.ui @@ -7,7 +7,7 @@ 0 0 390 - 205 + 211 @@ -527,7 +527,7 @@ background-color: rgb(205, 205, 205); - + 雷达告警 Qt::AlignCenter @@ -540,7 +540,7 @@ background-color: rgb(205, 205, 205); - + 紫外告警 Qt::AlignCenter @@ -553,7 +553,7 @@ background-color: rgb(205, 205, 205); - + 干扰弹投放 Qt::AlignCenter diff --git a/App/StatusUI/StatusUI.cpp b/App/StatusUI/StatusUI.cpp index 785c9a1..6d0aeb7 100644 --- a/App/StatusUI/StatusUI.cpp +++ b/App/StatusUI/StatusUI.cpp @@ -164,6 +164,12 @@ void StatusUI::setValue(QLabel *w,QString s) w->setText(s); } + +void StatusUI::setCurrentPlane(uint32_t id) +{ + ui->label_current_plane->setText(QString("当前飞机:%1").arg(id)); +} + void StatusUI::setMode(uint32_t pos,QVariant value1,QVariant value2) { switch (pos) { @@ -179,6 +185,7 @@ void StatusUI::setMode(uint32_t pos,QVariant value1,QVariant value2) + void StatusUI::setState(uint32_t pos, QVariant value1, QVariant value2) { switch (pos) { diff --git a/App/StatusUI/StatusUI.h b/App/StatusUI/StatusUI.h index ea13b18..5e01a28 100644 --- a/App/StatusUI/StatusUI.h +++ b/App/StatusUI/StatusUI.h @@ -36,7 +36,7 @@ public: public slots: - + void setCurrentPlane(uint32_t id); void setMode(uint32_t pos,QVariant value1,QVariant value2); void setState(uint32_t pos, QVariant value1, QVariant value2); void setServo(uint32_t pos,QVariant real,QVariant meas); diff --git a/App/StatusUI/StatusUI.ui b/App/StatusUI/StatusUI.ui index c50b10a..3d1c3ec 100644 --- a/App/StatusUI/StatusUI.ui +++ b/App/StatusUI/StatusUI.ui @@ -7,7 +7,7 @@ 0 0 573 - 754 + 768 @@ -989,6 +989,13 @@ color: rgb(0, 128, 0); + + + + 当前飞机: + + + diff --git a/App/ToolsUI/Payload/Payload.cpp b/App/ToolsUI/Payload/Payload.cpp new file mode 100644 index 0000000..5fdf7fd --- /dev/null +++ b/App/ToolsUI/Payload/Payload.cpp @@ -0,0 +1,1540 @@ +#include "Payload.h" +#include "ui_Payload.h" + +Payload::Payload(QWidget *parent) : + QWidget(parent), + ui(new Ui::Payload) +{ + ui->setupUi(this); + m_parent = parent; + + QFile file(":/qss/Payload.qss"); + file.open(QFile::ReadOnly); + QTextStream filetext(&file); + QString stylesheet = filetext.readAll(); + this->setStyleSheet(stylesheet); + file.close(); + + this->setWindowTitle(tr("Payload")); + + Config *cfg = new Config(); + + QHostAddress m_ra_address; + QHostAddress m_uv_address; + QVariant m_ra_out_port; + QVariant m_uv_out_port; + QVariant m_ra_in_port; + QVariant m_uv_in_port; + + cfg->get_Payload_RA_Info(&m_ra_address,&m_ra_out_port,&m_ra_in_port); + cfg->get_Payload_UV_Info(&m_uv_address,&m_uv_out_port,&m_uv_in_port); + + ra_address = m_ra_address; + uv_address = m_uv_address; + ra_out_port = m_ra_out_port.toInt(); + uv_out_port = m_uv_out_port.toInt(); + ra_in_port = m_ra_in_port.toInt(); + uv_in_port = m_uv_in_port.toInt(); + + delete cfg; + + + qint32 width = 300; + qint32 height = 35; + //============set size============ + setHeight(ui->lineEdit_CommandName,height); + setHeight(ui->pushButton_SendCommand,height); + setHeight(ui->pushButton_Cancel,height); + + setHeight(ui->pushButton_500W_Open,height); + setHeight(ui->pushButton_500W_Close,height); + + setHeight(ui->pushButton_radar_open,height); + setHeight(ui->pushButton_radar_close,height); + setHeight(ui->pushButton_radar_uploadthreat,height); + setHeight(ui->pushButton_radar_uploadwarning,height); + setHeight(ui->pushButton_radar_cancel_uploadwarning,height); + setHeight(ui->pushButton_radar_open_maneuver,height); + setHeight(ui->pushButton_radar_close_maneuver,height); + setHeight(ui->comboBox_radar_threat_level,height); + setHeight(ui->label_radar_status,height); + setHeight(ui->pushButton_radar_control_start,height); + setHeight(ui->pushButton_radar_control_stop,height); + setHeight(ui->spinBox_RA_ID,height); + setHeight(ui->pushButton_radar_fore_maneuver,height); + setHeight(ui->pushButton_radar_exit_fore_maneuver,height); + + setHeight(ui->pushButton_uv_power_on,height); + setHeight(ui->pushButton_uv_power_off,height); + setHeight(ui->pushButton_uv_open,height); + setHeight(ui->pushButton_uv_close,height); + //setHeight(ui->pushButton_uv_uploadthreat,height); + setHeight(ui->pushButton_uv_uploadwarning,height); + setHeight(ui->pushButton_uv_cancel_uploadwarning,height); + setHeight(ui->pushButton_uv_open_maneuver,height); + setHeight(ui->pushButton_uv_close_maneuver,height); + setHeight(ui->label_uv_status,height); + + setHeight(ui->pushButton_flare_self_check,height); + setHeight(ui->pushButton_flare_circuit_on,height); + setHeight(ui->pushButton_flare_circuit_off,height); + setHeight(ui->pushButton_flare_release_time_set,height); + setHeight(ui->pushButton_flare_on,height); + setHeight(ui->pushButton_flare_off,height); + setHeight(ui->pushButton_flare_manrelease,height); + setHeight(ui->pushButton_flare_manrelease_set,height); + setHeight(ui->pushButton_uv_fore_maneuver,height); + setHeight(ui->pushButton_uv_exit_fore_maneuver,height); + + setHeight(ui->pushButton_flare_release_bom1,height); + setHeight(ui->pushButton_flare_release_bom2,height); + setHeight(ui->pushButton_flare_release_bom3,height); + setHeight(ui->pushButton_flare_release_bom4,height); + setHeight(ui->pushButton_flare_release_bom5,height); + setHeight(ui->pushButton_flare_release_bom6,height); + + + setWidth(ui->pushButton_flare_release_time_set,150); + + setWidth(ui->pushButton_flare_release_bom1,150); + setWidth(ui->pushButton_flare_release_bom2,150); + setWidth(ui->pushButton_flare_release_bom3,150); + setWidth(ui->pushButton_flare_release_bom4,150); + setWidth(ui->pushButton_flare_release_bom5,150); + setWidth(ui->pushButton_flare_release_bom6,150); + + + setHeight(ui->lineEdit_radar_ip,height); + setHeight(ui->lineEdit_radar_port,height); + setHeight(ui->lineEdit_radar_port_in,height); + + setHeight(ui->lineEdit_uv_ip,height); + setHeight(ui->lineEdit_uv_port,height); + setHeight(ui->lineEdit_uv_port_in,height); + //setHeight(ui->lineEdit_uv_set_extent,height); + //setHeight(ui->lineEdit_uv_set_level,height); + + setHeight(ui->lineEdit_flare_manrelease_time,height); + setHeight(ui->lineEdit_flare_release_time,height); + + + QFont font; + font.setPointSize(18); + font.setFamily("SimHei"); + + QWidget::setFont(font); + + + setFont(ui->lineEdit_radar_ip,font); + setFont(ui->lineEdit_radar_port,font); + setFont(ui->lineEdit_radar_port_in,font); + setFont(ui->spinBox_RA_ID,font); + + setFont(ui->comboBox_radar_threat_level,font); + + setFont(ui->lineEdit_uv_ip,font); + setFont(ui->lineEdit_uv_port,font); + setFont(ui->lineEdit_uv_port_in,font); + //setFont(ui->lineEdit_uv_set_extent,font); + //setFont(ui->lineEdit_uv_set_level,font); + + setFont(ui->lineEdit_flare_manrelease_time,font); + setFont(ui->lineEdit_flare_release_time,font); + + + + + //============set color=========== + setColor(ui->label_radar_status,state::inital); + + setColor(ui->pushButton_radar_open,green_state); + setColor(ui->pushButton_radar_close,green_state); + setColor(ui->pushButton_radar_uploadthreat,green_state); + setColor(ui->pushButton_radar_uploadwarning,green_state); + setColor(ui->pushButton_radar_cancel_uploadwarning,green_state); + setColor(ui->pushButton_radar_open_maneuver,green_state); + setColor(ui->pushButton_radar_close_maneuver,red_state); + //setHeight(ui->pushButton_radar_fore_maneuver,height); + //setHeight(ui->pushButton_radar_exit_fore_maneuver,height); + + setColor(ui->label_uv_status,state::inital); + setColor(ui->pushButton_uv_power_on,green_state); + setColor(ui->pushButton_uv_power_off,red_state); + setColor(ui->pushButton_uv_open,green_state); + setColor(ui->pushButton_uv_close,green_state); + //setColor(ui->pushButton_uv_uploadthreat,green_state); + setColor(ui->pushButton_uv_uploadwarning,green_state); + setColor(ui->pushButton_uv_cancel_uploadwarning,green_state); + setColor(ui->pushButton_uv_open_maneuver,green_state); + setColor(ui->pushButton_uv_close_maneuver,red_state); + + setColor(ui->label_flare_bom1,state::inital); + setColor(ui->label_flare_bom2,state::inital); + setColor(ui->label_flare_bom3,state::inital); + setColor(ui->label_flare_bom4,state::inital); + setColor(ui->label_flare_bom5,state::inital); + setColor(ui->label_flare_bom6,state::inital); + setColor(ui->label_flare_ch1,state::inital); + setColor(ui->label_flare_ch2,state::inital); + setColor(ui->label_flare_ch3,state::inital); + setColor(ui->label_flare_ch4,state::inital); + setColor(ui->label_flare_ch5,state::inital); + setColor(ui->label_flare_ch6,state::inital); + + setColor(ui->label_flare_dev_id,state::inital); + setColor(ui->label_flare_fire_switch,state::inital); + setColor(ui->label_flare_power,state::inital); + setColor(ui->label_flare_release_switch,state::inital); + setColor(ui->label_flare_self_check_status,state::inital); + setColor(ui->label_flare_status,state::inital); + setColor(ui->pushButton_flare_self_check,green_state); + setColor(ui->pushButton_flare_circuit_on,green_state); + setColor(ui->pushButton_flare_circuit_off,red_state); + setColor(ui->pushButton_flare_release_time_set,green_state); + setColor(ui->pushButton_flare_on,green_state); + setColor(ui->pushButton_flare_off,red_state); + setColor(ui->pushButton_flare_manrelease,green_state); + setColor(ui->pushButton_flare_manrelease_set,green_state); + + + //===========set line edit=================== + QRegularExpressionValidator *validator = new QRegularExpressionValidator(QRegularExpression("^\\d+$"), this); + //ui->lineEdit_uv_set_extent->setValidator(validator); + //ui->lineEdit_uv_set_level->setValidator(validator); + + ui->lineEdit_flare_manrelease_time->setValidator(validator); + ui->lineEdit_flare_release_time->setValidator(validator); + + + + QRegularExpression ipRegex("^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"); + ui->lineEdit_radar_ip->setValidator(new QRegularExpressionValidator(ipRegex, this)); + ui->lineEdit_uv_ip->setValidator(new QRegularExpressionValidator(ipRegex, this)); + + + // 0-65536 的正则表达式(精确匹配) + QIntValidator *port_validator = new QIntValidator(0, 65535, this); + ui->lineEdit_radar_port->setValidator(port_validator); + ui->lineEdit_radar_port_in->setValidator(port_validator); + ui->lineEdit_uv_port->setValidator(port_validator); + ui->lineEdit_uv_port_in->setValidator(port_validator); + + + QRegularExpressionValidator *uv_validator = new QRegularExpressionValidator( + QRegularExpression("^([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$"), this); + //ui->lineEdit_uv_set_extent->setValidator(uv_validator); + //ui->lineEdit_uv_set_level->setValidator(uv_validator); + + + + ui->comboBox_radar_threat_level->addItem(QString("等级 0"),0); + ui->comboBox_radar_threat_level->addItem(QString("等级 1"),1); + ui->comboBox_radar_threat_level->addItem(QString("等级 2"),2); + ui->comboBox_radar_threat_level->addItem(QString("等级 3"),3); + ui->comboBox_radar_threat_level->addItem(QString("等级 4"),4); + ui->comboBox_radar_threat_level->addItem(QString("等级 5"),5); + ui->comboBox_radar_threat_level->setCurrentIndex(5); + + ui->lineEdit_radar_ip->setText(QHostAddress(ra_address).toString()); + ui->lineEdit_radar_port->setText(QString::number(ra_out_port)); + ui->lineEdit_radar_port_in->setText(QString::number(ra_in_port)); + ui->lineEdit_radar_ip->setPlaceholderText(QString("格式为 192.168.1.100")); + ui->lineEdit_radar_port->setPlaceholderText(QString("对方接收端口")); + ui->lineEdit_radar_port_in->setPlaceholderText(QString("本机监听端口")); + + + ui->lineEdit_uv_ip->setText(QHostAddress(uv_address).toString()); + ui->lineEdit_uv_port->setText(QString::number(uv_out_port)); + ui->lineEdit_uv_port_in->setText(QString::number(uv_in_port)); + ui->lineEdit_uv_ip->setPlaceholderText(QString("格式为 192.168.1.100")); + ui->lineEdit_uv_port->setPlaceholderText(QString("对方接收端口")); + ui->lineEdit_uv_port_in->setPlaceholderText(QString("本机监听端口")); + + + + //ui->lineEdit_uv_set_extent->setText(QString::number(32)); + //ui->lineEdit_uv_set_level->setText(QString::number(140)); + + ui->lineEdit_flare_release_time->setText(QString::number(1000)); + ui->lineEdit_flare_manrelease_time->setText(QString::number(1000)); + + setup_commands(); + setup_actions(); + + + +} + +Payload::~Payload() +{ + delete ui; +} + +void Payload::setColor(QWidget *w,QColor s) +{ + QString stype = "background-color: "; + stype.append(s.name()); + stype.append(";"); + + if(w) + { + w->setStyleSheet(stype); + w->style()->unpolish(w); + w->style()->polish(w); + } +} + +void Payload::setColor(QWidget *w,state s) +{ + w->setProperty("state",s); + w->style()->unpolish(w); + w->style()->polish(w); +} + +void Payload::setValue(QLabel *w,QString s) +{ + w->setText(s); +} + +void Payload::setHeight(QWidget *w,qint32 height) +{ + w->setFixedHeight(height); +} + +void Payload::setWidth(QWidget *w,qint32 width) +{ + w->setFixedWidth(width); +} + +void Payload::setSize(QWidget *w,qint32 width,qint32 height) +{ + w->setFixedSize(width,height); +} + +void Payload::setFont(QWidget *w,QFont font) +{ + w->setFont(font); +} + + + +void Payload::updateUI() +{ + +} + +void Payload::resizeEvent(QResizeEvent *event) +{ + //attChart->resizeEvent(event); + + +} + +void Payload::closeEvent(QCloseEvent *event) +{ + setFloat(); + event->ignore(); +} + +void Payload::setFloat(void) +{ + if(this->parent()) + { + this->setParent(nullptr); + QScreen *screen=QGuiApplication::primaryScreen (); + this->move((screen->availableGeometry().width()-this->width())/2,(screen->availableGeometry().height()-this->height())/2); + this->show(); + this->resize(m_parent->size()); + updateUI(); + } + else + { + this->setParent(m_parent); + this->move(0,0); + this->show(); + this->resize(m_parent->size()); + updateUI(); + } +} + + +void Payload::EnCapData(uint16_t seq,QByteArray data) +{ + //rwr:id为8 + //uvwd:id为9 + + switch (seq) { + case 0x08://rwr + { + + uint8_t msg[50]; + uint8_t len = 30; + + for(int i = 0;iisOpen())) + { + //qDebug() << "rwr send" << data; + RA_Sock->writeDatagram((const char *)msg,len,ra_address, ra_out_port); + } + + }break; + case 0x09://uvwd + { + + //qDebug() << " uvwd send" << data; + uint8_t msg[50]; + uint8_t len = 30; + + for(int i = 0;iisOpen()) + { + //qDebug() << "send" << data; + UV_Sock->writeDatagram((const char *)msg,len,uv_address, uv_out_port); + } + }break; + default: + break; + } + + +} + +void Payload::setUVWDState(mavlink_uvwdstate_t uvwdstate) +{ + //通过IP发送出去 +/* + if((UV_Sock)&&UV_Sock->isOpen()) + { + uint8_t msg[50]; + uint8_t len = 0; + + //0~2 + msg[len++] = 0xDD; + msg[len++] = 0x77; + msg[len++] = uvwdstate.device_id; + //3 + msg[len++] = uvwdstate.alarm_flag; + //4~6 + msg[len++] = uvwdstate.integration_time[0]; + msg[len++] = uvwdstate.integration_time[1]; + msg[len++] = uvwdstate.integration_time[2]; + //7~8 + msg[len++] = uvwdstate.image_avg_brightness; + msg[len++] = uvwdstate.image_avg_brightness >> 8; + //9~11 + msg[len++] = uvwdstate.device_state_1; + msg[len++] = uvwdstate.device_state_2; + msg[len++] = uvwdstate.device_state_3; + //12~28 + msg[len++] = 0; + msg[len++] = 0; + msg[len++] = 0; + + msg[len++] = 0; + msg[len++] = 0; + msg[len++] = 0; + msg[len++] = 0; + msg[len++] = 0; + + msg[len++] = 0; + msg[len++] = 0; + msg[len++] = 0; + msg[len++] = 0; + msg[len++] = 0; + + msg[len++] = 0; + msg[len++] = 0; + msg[len++] = 0; + msg[len++] = 0; + //SUN + uint8_t sum = 0; + for(int i = 0; i<29;i++) + { + sum += msg[i]; + } + + msg[len++] = sum; + + UV_Sock->writeDatagram((const char *)msg,len,uv_address, uv_out_port); + } +*/ +} + +void Payload::setRWRState(mavlink_rwrstate_t rwrstate) +{/* + if((RA_Sock)&&(RA_Sock->isOpen())) + { + uint8_t msg[50]; + uint8_t len = 0; + + //0~3 + msg[len++] = 0x90; + msg[len++] = 0xEB; + msg[len++] = rwrstate.device_id; + msg[len++] = rwrstate.msg_type; + //4~14 + msg[len++] = 0; + + msg[len++] = 0; + msg[len++] = 0; + msg[len++] = 0; + msg[len++] = 0; + msg[len++] = 0; + + msg[len++] = 0; + msg[len++] = 0; + msg[len++] = 0; + msg[len++] = 0; + msg[len++] = 0; + //15 + msg[len++] = rwrstate.threat_level; + //16-28 + msg[len++] = 0; + msg[len++] = 0; + msg[len++] = 0; + msg[len++] = 0; + + msg[len++] = 0; + msg[len++] = 0; + msg[len++] = 0; + msg[len++] = 0; + msg[len++] = 0; + + msg[len++] = 0; + msg[len++] = 0; + msg[len++] = 0; + msg[len++] = 0; + //SUN + uint8_t sum = 0; + for(int i = 2; i<29;i++) + { + sum += msg[i]; + } + + msg[len++] = sum; + + + RA_Sock->writeDatagram((const char *)msg,len,ra_address, ra_out_port); + } +*/ +} + +void Payload::setDLSState(mavlink_dlsstate_t dlsstate) +{ + //================设备ID============== + ui->label_flare_dev_id->setText(QString::number(dlsstate.device_id)); + + //=================自检状态============= + uint8_t seft_check = dlsstate.self_test_state; + if(seft_check == 0x00) + { + ui->label_flare_self_check_status->setText(QString("上电未自检")); + } + else if(seft_check == 0x01) + { + ui->label_flare_self_check_status->setText(QString("自检正常")); + } + else if(seft_check == 0x02) + { + ui->label_flare_self_check_status->setText(QString("自检异常")); + } + + //==================================== + uint8_t fire_switch = dlsstate.power_pyro_safety_state & 0x03; + if(fire_switch == 0x00) + { + ui->label_flare_fire_switch->setText(QString("火工关")); + } + else if(fire_switch == 0x01) + { + ui->label_flare_fire_switch->setText(QString("火工异常")); + } + else if(fire_switch == 0x03) + { + ui->label_flare_fire_switch->setText(QString("火工开")); + } + + //==================================== + uint8_t power_voltage = (dlsstate.power_pyro_safety_state & 0x0C) >> 2; + if(power_voltage == 0x00) + { + ui->label_flare_power->setText(QString("电压正常")); + } + else if(power_voltage == 0x01) + { + ui->label_flare_power->setText(QString("电压异常")); + } + + //==================================== + uint8_t release_switch = (dlsstate.power_pyro_safety_state & 0x10) >> 4; + if(release_switch == 0x00) + { + ui->label_flare_release_switch->setText(QString("关")); + } + else if(release_switch == 0x01) + { + ui->label_flare_release_switch->setText(QString("开")); + } + + //==================================== + uint8_t work_state = dlsstate.work_state; + if(work_state == 0x00) + { + ui->label_flare_status->setText(QString("待机")); + } + else if(work_state == 0x40) + { + ui->label_flare_status->setText(QString("自检测试")); + } + else if(work_state == 0xff) + { + ui->label_flare_status->setText(QString("手动触发")); + } + else if((work_state >= 0x01) && (work_state <= 0x0F)) + { + ui->label_flare_status->setText(QString("执行任务 %1").arg(work_state)); + } + + //==================================== + //弹正常显示绿色,异常显示红色 + uint8_t cartridge1 = dlsstate.cartridge_state[2]; + + setColor(ui->label_flare_bom1,(cartridge1 & 0x01)?(state::failure):(state::success)); + setColor(ui->label_flare_bom2,(cartridge1 & 0x02)?(state::failure):(state::success)); + setColor(ui->label_flare_bom3,(cartridge1 & 0x04)?(state::failure):(state::success)); + setColor(ui->label_flare_bom4,(cartridge1 & 0x08)?(state::failure):(state::success)); + setColor(ui->label_flare_bom5,(cartridge1 & 0x10)?(state::failure):(state::success)); + setColor(ui->label_flare_bom6,(cartridge1 & 0x20)?(state::failure):(state::success)); + + + //==================================== + //有弹显示绿色,无弹显示红色 + uint8_t channel1 = dlsstate.channel_cartridge_state[2]; + + setColor(ui->label_flare_ch1,(channel1 & 0x01)?(state::success):(state::failure)); + setColor(ui->label_flare_ch2,(channel1 & 0x02)?(state::success):(state::failure)); + setColor(ui->label_flare_ch3,(channel1 & 0x04)?(state::success):(state::failure)); + setColor(ui->label_flare_ch4,(channel1 & 0x08)?(state::success):(state::failure)); + setColor(ui->label_flare_ch5,(channel1 & 0x10)?(state::success):(state::failure)); + setColor(ui->label_flare_ch6,(channel1 & 0x20)?(state::success):(state::failure)); + +} + +void Payload::setPayloadStatus(mavlink_payloadalarmstate_t payloadalarmstate) +{ + if(payloadalarmstate.rwr_alarm_flag) + { + Radar_Status(1); + } + else + { + Radar_Status(0); + } + + + if(payloadalarmstate.uvwd_alarm_flag) + { + UV_Status(1); + } + else + { + UV_Status(0); + } + +} + +void Payload::on_pushButton_Cancel_clicked() +{ + ui->lineEdit_CommandName->clear(); + m_name.clear(); + m_param1 = 0; + m_param2 = 0; + m_param3 = 0; + m_param4 = 0; + m_param5 = 0; + m_param6 = 0; + m_param7 = 0; + m_command = 0; + m_confirm = 0; +} + + +void Payload::on_commandClicked(QString name, + float param1,float param2,float param3,float param4, + float param5,float param6,float param7, + uint16_t command,uint8_t confirmation) +{ + + m_name = name; + m_param1 = param1; + m_param2 = param2; + m_param3 = param3; + m_param4 = param4; + m_param5 = param5; + m_param6 = param6; + m_param7 = param7; + m_command = command; + m_confirm = confirmation; + + ui->lineEdit_CommandName->setText(name); + + cmdType = 0; + } + +void Payload::on_pushButton_SendCommand_clicked() +{ + if(cmdType == 0) + { + if(m_command == 0) + { + emit showMessage(tr("please set command first ,command :%1").arg(m_command)); + return; + } + } + + if(cmdType == 0) + { + emit showMessage(tr("发送指令 ") + QString::number(m_command) + " " + m_name,5000); + emit cmd_long(m_param1,m_param2,m_param3,m_param4,m_param5,m_param6,m_param7,m_command,m_confirm); + + } + else if(cmdType == 1) + { + + //emit showMessage(tr("修改参数 ") + " " + m_id.toLatin1().data() + " " + QString::number(m_value),5000); + //emit WriteCmd(m_sysid,m_compid,m_id.toLatin1().data(),m_type,param_getvalue(m_value,m_type)); + + + + } +} + +void Payload::RA_Parser_init(void) +{ + RA_Parser_State = 0; + RA_Index = 0; + RA_SUM = 0; +} + +qint8 Payload::RA_ParseByte(uint8_t byte) +{ + qint8 index = -1; + switch (RA_Parser_State) { + case 0: + if (byte == 0x90) { + + RA_Index = 0; + RA_Frame[RA_Index] = byte; + RA_Parser_State = 1; + } + break; + case 1: + if (byte == 0xEB) { + RA_Index ++; + RA_Frame[RA_Index] = byte; + RA_SUM = 0; + RA_Parser_State = 2; + } else { + // 帧头2错误,重置并重新处理当前字节(作为新的帧头1) + RA_Parser_State = 0; + } + break; + + case 2: + if (RA_Index < 11) { + RA_Index ++; + RA_Frame[RA_Index] = byte; + RA_SUM += byte; + + if(RA_Index == 11) + { + RA_Parser_State = 3; + } + + } + break; + case 3: + if(RA_SUM == byte)//解析成功,发送数据 + { + RA_Index++; + RA_Frame[RA_Index] = byte; + index = RA_Frame[2]; + + } + RA_Parser_State = 0; + break; + } + return index; +} + +void Payload::UV_Parser_init(void) +{ + UV_Parser_State = 0; + UV_Index = 0; + UV_SUM = 0; +} + +qint8 Payload::UV_ParseByte(uint8_t byte) +{ + qint8 index = -1; + switch (UV_Parser_State) { + case 0: + if (byte == 0xDD) { + UV_SUM = 0; + UV_Index = 0; + UV_Frame[UV_Index] = byte; + UV_SUM = byte; + UV_Parser_State = 1; + } + break; + case 1: + if (byte == 0x77) { + UV_Index ++; + UV_Frame[UV_Index] = byte; + UV_SUM += byte; + UV_Parser_State = 2; + } else { + // 帧头2错误,重置并重新处理当前字节(作为新的帧头1) + UV_Parser_State = 0; + } + break; + + case 2: + if (UV_Index < 11) { + UV_Index ++; + UV_Frame[UV_Index] = byte; + UV_SUM += byte; + + if(UV_Index == 11) + { + UV_Parser_State = 3; + } + + } + break; + case 3: + if(UV_SUM == byte)//解析成功,发送数据 + { + UV_Index++; + UV_Frame[UV_Index] = byte; + index = UV_Frame[2]; + } + UV_Parser_State = 0; + break; + } + + return index; +} + + + +void Payload::setup_commands(void) +{ + //set network + + + + + + //========power=========================== + connect(ui->pushButton_500W_Open,&QPushButton::clicked,this,[=](){ + on_commandClicked(QString("电池500W输出开"),102,1,0,0,0,0,0,180,0); + }); + + connect(ui->pushButton_500W_Close,&QPushButton::clicked,this,[=](){ + on_commandClicked(QString("电池500W输出关"),102,0,0,0,0,0,0,180,0); + }); + + //========radar============================= + connect(ui->pushButton_radar_open,&QPushButton::clicked,this,[=](){ + if(ui->lineEdit_radar_ip->text().isEmpty() || ui->lineEdit_radar_port->text().isEmpty() || ui->lineEdit_radar_port_in->text().isEmpty()) + { + showMessage("请设置正确的IP号和端口号",5000); + return; + } + + + //输出开启 + QString ip = ui->lineEdit_radar_ip->text(); + quint16 port = ui->lineEdit_radar_port->text().toUInt(); + + ra_in_port = ui->lineEdit_radar_port_in->text().toUInt(); + + ra_address.setAddress(ip); + ra_out_port = port; + + + Config *cfg = new Config(); + + cfg->set_Payload_RA_Info(ra_address,QVariant(ra_out_port),QVariant(ra_in_port)); + + delete cfg; + + if(!RA_Sock) + { + RA_Sock = new QUdpSocket(this); + + + bool rst = RA_Sock->bind(QHostAddress::AnyIPv4,ra_in_port); + + qDebug() << rst; + + if(rst) + { + if(RA_Sock->open(QIODevice::ReadWrite)) + { + qDebug() << "信息输出端口打开正常"; + showMessage("雷达信息输出端口打开正常",5000); + } + + + + + connect(RA_Sock,&QUdpSocket::readyRead,this,[=](){ + //收到其他地面站数据 + /* + + 帧头 1 帧头 2 设备识别码 数据 和校验 + 0xDD 0x77 0x02 控制数据 前12个字节累加和 + lbyte lbyte lbyte 9byte 1byte + + */ + + + //发送180 113 4 设备号 报文类型 工作状态 + //发送180 113 5 设备号 报文类型 工作状态 + + + + QObject *obj = sender(); + + if(obj) + { + QUdpSocket *clientsock = qobject_cast(obj); + if(clientsock) + { + while (clientsock->hasPendingDatagrams()) { + QByteArray datagram; + datagram.resize(clientsock->pendingDatagramSize()); + clientsock->readDatagram(datagram.data(), datagram.size()); + + //qDebug() << "ra recive" << datagram; + + qint8 rst = -1; + for (QByteArray::const_iterator i = datagram.cbegin(); i != datagram.cend(); ++i) + { + + rst = RA_ParseByte(*i); + if(rst != -1) + { + /* + //发送180 114 4(5地面站发送) 识别码 命令字节 强度档位 范围档位 + uint8_t dev = datagram[2]; + uint8_t type = datagram[3] & 0x0F; + uint8_t state = ((datagram[3] & 0xF0) == 0x10)?(0x01):(0x00); + + qDebug() << "ra recive" << dev << type << state; + + on_commandClicked(QString("雷达告警控制指令"),113,4,dev,type,state,0,0,180,0); + on_pushButton_SendCommand_clicked(); + */ + + //发到encap + + QByteArray rwr_encap; + + for(int i = 0;i< 13;i++) + { + rwr_encap[i] = RA_Frame[i]; + } + + //qDebug() << "send ra" << rwr_encap; + emit send_EnCapData(0x08,rwr_encap); + + } + } + } + } + } + + }); + + } + else + { + qDebug() << "绑定失败:" << RA_Sock->errorString(); + showMessage("绑定失败:" + RA_Sock->errorString(),5000); + } + } + }); + + connect(ui->pushButton_radar_close,&QPushButton::clicked,this,[=](){ + //输出关闭 + if(RA_Sock) + { + RA_Sock->abort(); + delete RA_Sock; + RA_Sock = nullptr; + showMessage("雷达信息输出端口关闭",5000); + } + }); + + connect(ui->pushButton_radar_uploadthreat,&QPushButton::clicked,this,[=](){ + uint8_t level = ui->comboBox_radar_threat_level->currentData(Qt::UserRole).toInt(); + on_commandClicked(QString("上传威胁等级"),113,1,level,0,0,0,0,180,0); + qDebug() << level; + }); + + connect(ui->pushButton_radar_uploadwarning,&QPushButton::clicked,this,[=](){ + on_commandClicked(QString("注入雷达告警信号"),113,2,1,0,0,0,0,180,0); + }); + + connect(ui->pushButton_radar_cancel_uploadwarning,&QPushButton::clicked,this,[=](){ + on_commandClicked(QString("取消注入雷达告警信号"),113,2,0,0,0,0,0,180,0); + }); + + connect(ui->pushButton_radar_open_maneuver,&QPushButton::clicked,this,[=](){ + on_commandClicked(QString("自动逃逸机动开启"),113,3,1,0,0,0,0,180,0); + }); + + connect(ui->pushButton_radar_close_maneuver,&QPushButton::clicked,this,[=](){ + on_commandClicked(QString("自动逃逸机动关闭"),113,3,0,0,0,0,0,180,0); + }); + + connect(ui->pushButton_radar_control_start,&QPushButton::clicked,this,[=](){ + + //发送180 114 4(5地面站发送) 识别码 命令字节 强度档位 范围档位 + uint8_t dev = ui->spinBox_RA_ID->value(); + uint8_t type = 4; + uint8_t state = 1; + + on_commandClicked(QString("地面站雷达告警控制指令"),113,5,dev,type,state,0,0,180,0); + }); + + connect(ui->pushButton_radar_control_stop,&QPushButton::clicked,this,[=](){ + + //发送180 114 4(5地面站发送) 识别码 命令字节 强度档位 范围档位 + uint8_t dev = ui->spinBox_RA_ID->value(); + uint8_t type = 4; + uint8_t state = 0; + + on_commandClicked(QString("地面站雷达告警控制指令"),113,5,dev,type,state,0,0,180,0); + }); + + + connect(ui->pushButton_radar_fore_maneuver,&QPushButton::clicked,this,[=](){ + + on_commandClicked(QString("地面站雷达告警控制指令"),112,3,0,0,0,0,0,180,0); + }); + + connect(ui->pushButton_radar_exit_fore_maneuver,&QPushButton::clicked,this,[=](){ + + on_commandClicked(QString("地面站雷达告警控制指令"),112,2,0,0,0,0,0,180,0); + }); + + + //==============uv========================= + connect(ui->pushButton_uv_open,&QPushButton::clicked,this,[=](){ + if(ui->lineEdit_uv_ip->text().isEmpty() || ui->lineEdit_uv_port->text().isEmpty() || ui->lineEdit_uv_port_in->text().isEmpty()) + { + showMessage("请设置正确的IP号和端口号",5000); + return; + } + + //输出开启 + QString ip = ui->lineEdit_uv_ip->text(); + quint16 port = ui->lineEdit_uv_port->text().toUInt(); + + uv_in_port = ui->lineEdit_uv_port_in->text().toUInt(); + + uv_address.setAddress(ip); + uv_out_port = port; + + Config *cfg = new Config(); + + cfg->set_Payload_UV_Info(uv_address,QVariant(uv_out_port),QVariant(uv_in_port)); + + delete cfg; + + + if(!UV_Sock) + { + UV_Sock = new QUdpSocket(this); + int rst = UV_Sock->bind(QHostAddress::AnyIPv4,uv_in_port); + if(rst) + { + if(UV_Sock->open(QIODevice::ReadWrite)) + { + qDebug() << "信息输出端口打开正常"; + showMessage("紫外信息输出端口打开正常",5000); + UV_Parser_init(); + } + + + + connect(UV_Sock,&QUdpSocket::readyRead,this,[=](){ + + //收到其他地面站数据 + /* + + 帧头 1 帧头 2 设备识别码 数据 和校验 + 0xDD 0x77 0x02 控制数据 前12个字节累加和 + lbyte lbyte lbyte 9byte 1byte + + */ + + QObject *obj = sender(); + + if(obj) + { + QUdpSocket *clientsock = qobject_cast(obj); + if(clientsock) + { + while (clientsock->hasPendingDatagrams()) { + QByteArray datagram; + datagram.resize(clientsock->pendingDatagramSize()); + clientsock->readDatagram(datagram.data(), datagram.size()); + + + //qDebug() << "uv recive" << datagram; + qint8 rst = -1; + for (QByteArray::const_iterator i = datagram.cbegin(); i != datagram.cend(); ++i) + { + + rst = UV_ParseByte(*i); + if(rst != -1) + { + + /* + //发送180 114 5 识别码 命令字节 强度档位 范围档位 + uint8_t id = datagram[2]; + uint8_t cmd = datagram[3]; + uint8_t extent = datagram[4]; + uint8_t level = datagram[5]; + + on_commandClicked(QString("紫外告警控制指令"),114,5,id,cmd,extent,level,0,180,0); + on_pushButton_SendCommand_clicked(); + */ + + //发送到encap + + QByteArray uv_encap; + + for(int i = 0;i< 13;i++) + { + uv_encap[i] = UV_Frame[i]; + } + + //qDebug() << "send uv " << uv_encap; + emit send_EnCapData(0x09,uv_encap); + + } + } + } + } + } + + + + }); + } + else + { + qDebug() << "绑定失败:" << UV_Sock->errorString(); + showMessage("绑定失败:" + UV_Sock->errorString(),5000); + } + } + }); + + connect(ui->pushButton_uv_close,&QPushButton::clicked,this,[=](){ + //输出关闭 + if(UV_Sock) + { + UV_Sock->abort(); + delete UV_Sock; + UV_Sock = nullptr; + showMessage("紫外信息输出端口关闭",5000); + } + }); + + connect(ui->pushButton_uv_power_on,&QPushButton::clicked,this,[=](){ + on_commandClicked(QString("上电"),114,1,1,0,0,0,0,180,0); + }); + + connect(ui->pushButton_uv_power_off,&QPushButton::clicked,this,[=](){ + on_commandClicked(QString("下电"),114,1,0,0,0,0,0,180,0); + }); + + /*connect(ui->pushButton_uv_uploadthreat,&QPushButton::clicked,this,[=](){ + uint8_t level = ui->lineEdit_uv_set_level->text().toInt(); + uint8_t extent = ui->lineEdit_uv_set_extent->text().toInt(); + + on_commandClicked(QString("上传阈值"),114,2,level,extent,0,0,0,180,0); + }); + */ + + connect(ui->pushButton_uv_uploadwarning,&QPushButton::clicked,this,[=](){ + on_commandClicked(QString("注入紫外告警信号"),114,3,1,0,0,0,0,180,0); + }); + + connect(ui->pushButton_uv_cancel_uploadwarning,&QPushButton::clicked,this,[=](){ + on_commandClicked(QString("取消注入紫外告警信号"),114,3,0,0,0,0,0,180,0); + }); + + connect(ui->pushButton_uv_open_maneuver,&QPushButton::clicked,this,[=](){ + on_commandClicked(QString("自动逃逸机动开启"),114,4,1,0,0,0,0,180,0); + }); + + connect(ui->pushButton_uv_close_maneuver,&QPushButton::clicked,this,[=](){ + on_commandClicked(QString("自动逃逸机动关闭"),114,4,0,0,0,0,0,180,0); + }); + + + connect(ui->pushButton_uv_fore_maneuver,&QPushButton::clicked,this,[=](){ + + on_commandClicked(QString("地面站雷达告警控制指令"),112,3,0,0,0,0,0,180,0); + }); + + connect(ui->pushButton_uv_exit_fore_maneuver,&QPushButton::clicked,this,[=](){ + + on_commandClicked(QString("地面站雷达告警控制指令"),112,2,0,0,0,0,0,180,0); + }); + + //================================================ + connect(ui->pushButton_flare_self_check,&QPushButton::clicked,this,[=](){ + on_commandClicked(QString("自检"),115,1,0,0,0,0,0,180,0); + }); + + connect(ui->pushButton_flare_circuit_on,&QPushButton::clicked,this,[=](){ + on_commandClicked(QString("保险开"),115,2,1,0,0,0,0,180,0); + }); + + connect(ui->pushButton_flare_circuit_off,&QPushButton::clicked,this,[=](){ + on_commandClicked(QString("保险关"),115,2,0,0,0,0,0,180,0); + }); + + connect(ui->pushButton_flare_release_time_set,&QPushButton::clicked,this,[=](){ + uint16_t time = ui->lineEdit_flare_release_time->text().toInt(); + + if(time > 60000) + { + time = 60000; + } + + on_commandClicked(QString("自动释放时间间隔(ms)设置"),115,3,time,0,0,0,0,180,0); + }); + + connect(ui->pushButton_flare_on,&QPushButton::clicked,this,[=](){ + on_commandClicked(QString("告警后自动释放-开"),115,4,1,0,0,0,0,180,0); + }); + + connect(ui->pushButton_flare_off,&QPushButton::clicked,this,[=](){ + on_commandClicked(QString("告警后自动释放-关"),115,4,0,0,0,0,0,180,0); + }); + + connect(ui->pushButton_flare_manrelease_set,&QPushButton::clicked,this,[=](){ + uint16_t time = ui->lineEdit_flare_manrelease_time->text().toInt(); + + if(time > 60000) + { + time = 60000; + } + + on_commandClicked(QString("手动释放时间间隔(ms)设置"),115,5,time,0,0,0,0,180,0); + }); + + connect(ui->pushButton_flare_manrelease,&QPushButton::clicked,this,[=](){ + on_commandClicked(QString("手动释放"),115,6,1,flare_release_status,0,0,0,180,0); + }); + + //================================================= + connect(ui->pushButton_flare_release_bom1,&QPushButton::clicked,this,[=](){ + + quint32 bitmask = 0x01; + + if((flare_release_status & bitmask) == 0x00) + { + flare_release_status |= bitmask; + } + else + { + flare_release_status &= ~bitmask; + } + + setColor(ui->pushButton_flare_release_bom1,(flare_release_status & bitmask)?(green_state):(white_state)); + + qDebug() << "flare_release_status" << flare_release_status; + }); + + connect(ui->pushButton_flare_release_bom2,&QPushButton::clicked,this,[=](){ + + quint32 bitmask = 0x02; + + if((flare_release_status & bitmask) == 0x00) + { + flare_release_status |= bitmask; + } + else + { + flare_release_status &= ~bitmask; + } + + setColor(ui->pushButton_flare_release_bom2,(flare_release_status & bitmask)?(green_state):(white_state)); + qDebug() << "flare_release_status" << flare_release_status; + }); + + connect(ui->pushButton_flare_release_bom3,&QPushButton::clicked,this,[=](){ + + quint32 bitmask = 0x04; + + if((flare_release_status & bitmask) == 0x00) + { + flare_release_status |= bitmask; + } + else + { + flare_release_status &= ~bitmask; + } + + setColor(ui->pushButton_flare_release_bom3,(flare_release_status & bitmask)?(green_state):(white_state)); + qDebug() << "flare_release_status" << flare_release_status; + }); + + connect(ui->pushButton_flare_release_bom4,&QPushButton::clicked,this,[=](){ + + quint32 bitmask = 0x08; + + if((flare_release_status & bitmask) == 0x00) + { + flare_release_status |= bitmask; + } + else + { + flare_release_status &= ~bitmask; + } + + setColor(ui->pushButton_flare_release_bom4,(flare_release_status & bitmask)?(green_state):(white_state)); + qDebug() << "flare_release_status" << flare_release_status; + }); + + connect(ui->pushButton_flare_release_bom5,&QPushButton::clicked,this,[=](){ + + quint32 bitmask = 0x10; + + if((flare_release_status & bitmask) == 0x00) + { + flare_release_status |= bitmask; + } + else + { + flare_release_status &= ~bitmask; + } + + setColor(ui->pushButton_flare_release_bom5,(flare_release_status & bitmask)?(green_state):(white_state)); + qDebug() << "flare_release_status" << flare_release_status; + }); + + connect(ui->pushButton_flare_release_bom6,&QPushButton::clicked,this,[=](){ + + quint32 bitmask = 0x20; + + if((flare_release_status & bitmask) == 0x00) + { + flare_release_status |= bitmask; + } + else + { + flare_release_status &= ~bitmask; + } + + setColor(ui->pushButton_flare_release_bom6,(flare_release_status & bitmask)?(green_state):(white_state)); + qDebug() << "flare_release_status" << flare_release_status; + }); + + + + + +} + +void Payload::setup_actions(void) +{ + ra_timer = new QTimer(); + + ra_timer->setInterval(500); + + connect(ra_timer,&QTimer::timeout,this,[=](){ + if((ra_status & 0x01) == 0x01)//前16为做状态,后16后做指示 + { + if((ra_status & 0x010000) == 0x010000) + { + setColor(ui->label_radar_status,state::warning); + ra_status &= ~0x010000; + } + else + { + setColor(ui->label_radar_status,state::inital); + ra_status |= 0x010000; + } + } + else + { + ra_timer->stop(); + setColor(ui->label_radar_status,state::inital); + } + }); + + + uv_timer = new QTimer(); + + uv_timer->setInterval(500); + + connect(uv_timer,&QTimer::timeout,this,[=](){ + if((uv_status & 0x01) == 0x01)//前16为做状态,后16后做指示 + { + if((uv_status & 0x010000) == 0x010000) + { + setColor(ui->label_uv_status,state::warning); + uv_status &= ~0x010000; + } + else + { + setColor(ui->label_uv_status,state::inital); + uv_status |= 0x010000; + } + } + else + { + uv_timer->stop(); + setColor(ui->label_uv_status,state::inital); + } + }); +} + + + +void Payload::Radar_Status(quint32 flag) +{ + if(flag == 0x01)//闪烁 + { + if(!ra_timer->isActive()) + { + ra_timer->start(); + } + ra_status |= 0x01; + } + else + { + ra_status &= ~0x01; + } +} + +void Payload::UV_Status(quint32 flag) +{ + if(flag == 0x01)//闪烁 + { + if(!uv_timer->isActive()) + { + uv_timer->start(); + } + uv_status |= 0x01; + } + else + { + uv_status &= ~0x01; + } +} + + +void Payload::commandAccepted(bool flag,uint16_t command,uint8_t result) +{ + bool isFundSender = false; + QString string; + //找到消息的发送者 + + + //QObjectList list = ui->frame->children(); + + // qDebug() << list; + + //qDebug() << CommandBox.findChildren(); + + + if(command == m_command) + { + isFundSender = true; + + + switch (result) { + case MAV_RESULT_ACCEPTED: + string.append(tr("%1,%2 accepted and executed").arg(command).arg(m_name)); + break; + case MAV_RESULT_TEMPORARILY_REJECTED: + string.append(tr("%1,%2 rejected").arg(command).arg(m_name)); + break; + case MAV_RESULT_DENIED: + string.append(tr("%1,%2 permanently denied").arg(command).arg(m_name)); + break; + case MAV_RESULT_UNSUPPORTED: + string.append(tr("%1,%2 unsupported").arg(command).arg(m_name)); + break; + case MAV_RESULT_FAILED: + string.append(tr("%1,%2 failed").arg(command).arg(m_name)); + break; + case MAV_RESULT_IN_PROGRESS: + string.append(tr("%1,%2 being executed").arg(command).arg(m_name)); + break; + case 0xFE: + string.append(tr("%1,%2 time out").arg(command).arg(m_name)); + break; + case 0xFF: + string.append(tr("Please do not operate too fast, the last instruction has not been sent")); + break; + default: + break; + } + + ui->lineEdit_CommandName->clear(); + m_name.clear(); + m_param1 = 0; + m_param2 = 0; + m_param3 = 0; + m_param4 = 0; + m_param5 = 0; + m_param6 = 0; + m_param7 = 0; + m_command = 0; + m_confirm = 0; + + if(string.size() > 0) + { + + emit say(string); + + + emit showMessage(string); + } + + + } + + /* + if(string.size() > 0) + { + QTextToSpeech *tts = new QTextToSpeech(); + tts->say(string); + tts->deleteLater(); + emit showMessage(string); + } + */ +} + + diff --git a/App/ToolsUI/Payload/Payload.h b/App/ToolsUI/Payload/Payload.h new file mode 100644 index 0000000..f34c890 --- /dev/null +++ b/App/ToolsUI/Payload/Payload.h @@ -0,0 +1,194 @@ +#ifndef PAYLOAD_H +#define PAYLOAD_H + +#include +#include "QMouseEvent" +#include "QDebug" +#include "QAction" +#include "QContextMenuEvent" +#include "QMenu" +#include "QColor" +#include "QLabel" +#include "mavlink.h" +#include "QStyle" +#include "QDebug" +#include "QScreen" +#include + +#include +#include +#include + +#include "Chart.h" +#include "QCheckBox" +#include "QColor" + +#include "QRegularExpressionValidator" + +#include "QUdpSocket" + +#include "mybutton.h" + +#include "Config/Config.h" + +#include "QTextToSpeech" + +#define green_state QColor("#55FF7F") +#define gray_state QColor("#BDBDBD") +#define white_state QColor("#FFFFFF") +#define red_state QColor("#FF8C8E") + +namespace Ui { +class Payload; +} + +class Payload : public QWidget +{ + Q_OBJECT + +public: + + enum state{ + success = 0, + failure = 1, + warning = 2, + inital = 3, + }; + + explicit Payload(QWidget *parent = nullptr); + ~Payload(); + + void setColor(QWidget *w, QColor s); + void setColor(QWidget *w,state s); + void setValue(QLabel *w,QString s); + void setHeight(QWidget *w,qint32 height); + void setWidth(QWidget *w,qint32 width); + void setSize(QWidget *w,qint32 width,qint32 height); + void setFont(QWidget *w,QFont font); + + void setFloat(void); + + void updateUI(); + + void setup_commands(void); + void setup_actions(void); + + + void setUVWDState(mavlink_uvwdstate_t uvwdstate); + void setRWRState(mavlink_rwrstate_t rwrstate); + void setDLSState(mavlink_dlsstate_t dlsstate); + void setPayloadStatus(mavlink_payloadalarmstate_t payloadalarmstate); + + + + void Radar_Status(quint32 flag); + void UV_Status(quint32 flag); + + + void RA_Parser_init(void); + qint8 RA_ParseByte(uint8_t byte); + + void UV_Parser_init(void); + qint8 UV_ParseByte(uint8_t byte); + +public slots: + void EnCapData(uint16_t seq, QByteArray data); + + void commandAccepted(bool flag,uint16_t command,uint8_t result); + +private slots: + + void on_commandClicked(QString name, + float param1,float param2,float param3,float param4, + float param5,float param6,float param7, + uint16_t command,uint8_t confirmation); + + + + + + void on_pushButton_SendCommand_clicked(); + + void on_pushButton_Cancel_clicked(); + + + +signals: + void showMessage(const QString &message,int TimeOut = 0); + void cmd_int(float param1, float param2, float param3, float param4, int x, int y, float z); + void cmd_long( float param1,float param2,float param3,float param4,float param5,float param6,float param7,uint16_t command,uint8_t confirmation); + + void WriteCmd(uint8_t m_sysid, uint8_t m_compid , const char *id, uint8_t type, float value); + + void say(QString str); + + void send_EnCapData(int seq, QByteArray data); + +protected: + void resizeEvent(QResizeEvent *event); + void closeEvent(QCloseEvent *event); + +private: + Ui::Payload *ui; + QWidget *m_parent; + + + QString m_name = 0; + float m_param1 = 0; + float m_param2 = 0; + float m_param3 = 0; + float m_param4 = 0; + float m_param5 = 0; + float m_param6 = 0; + float m_param7 = 0; + uint16_t m_command = 0; + uint8_t m_confirm = 0; + + uint32_t cmdType = 0;//0 cmd 1 param + + + + QTimer *ra_timer = nullptr; + QTimer *uv_timer = nullptr; + + + quint32 ra_status = 0; + quint32 uv_status = 0; + + quint32 flare_release_status = 0; + + + + QUdpSocket *RA_Sock = nullptr; + QUdpSocket *UV_Sock = nullptr; + + QHostAddress ra_address = QHostAddress("192.168.1.100"); + QHostAddress uv_address = QHostAddress("192.168.1.100"); + quint16 ra_out_port = 9010; + quint16 uv_out_port = 9020; + quint16 ra_in_port = 10010; + quint16 uv_in_port = 10020; + + + quint8 RA_Parser_State = 0; + + uint8_t RA_Frame[50]; + uint8_t RA_Index = 0; + uint8_t RA_SUM = 0; + + quint8 UV_Parser_State = 0; + + uint8_t UV_Frame[50]; + uint8_t UV_Index = 0; + uint8_t UV_SUM = 0; + + QMap cmdmap; + + bool isWaitPitotAck = false; + bool isWaitPitotClose = true; + bool isWaitTracerAck = false; + + +}; + +#endif // PAYLOAD_H diff --git a/App/ToolsUI/Payload/Payload.pri b/App/ToolsUI/Payload/Payload.pri new file mode 100644 index 0000000..a3f2cee --- /dev/null +++ b/App/ToolsUI/Payload/Payload.pri @@ -0,0 +1,44 @@ + +QT += opengl +QT += network +QT += sql +QT += svg +QT += quickwidgets +QT += quick +QT += qml + +RESOURCES += \ + $$PWD/Payload.qrc + +FORMS += \ + $$PWD/Payload.ui + +HEADERS += \ + $$PWD/Payload.h \ + $$PWD/dev/flare_device.h \ + $$PWD/dev/payload_protocol.h \ + $$PWD/dev/radar_warning_device.h \ + $$PWD/dev/uv_warning_device.h \ + $$PWD/mybutton.h + + +SOURCES += \ + $$PWD/Payload.cpp \ + $$PWD/dev/flare_device.cpp \ + $$PWD/dev/radar_warning_device.cpp \ + $$PWD/dev/uv_warning_device.cpp \ + $$PWD/mybutton.cpp + +INCLUDEPATH += $$PWD + + + + + + + + + + + + diff --git a/App/ToolsUI/Payload/Payload.qrc b/App/ToolsUI/Payload/Payload.qrc new file mode 100644 index 0000000..0e6e0cc --- /dev/null +++ b/App/ToolsUI/Payload/Payload.qrc @@ -0,0 +1,12 @@ + + + Payload.qss + + + fail.png + normal.png + fail_rect.png + success_rect.png + warning_rect.png + + diff --git a/App/ToolsUI/Payload/Payload.qss b/App/ToolsUI/Payload/Payload.qss new file mode 100644 index 0000000..4fc8c1a --- /dev/null +++ b/App/ToolsUI/Payload/Payload.qss @@ -0,0 +1,90 @@ + + +.QFrame { + background-color: #FFFFFF; +} + + +.QPushButton { + background-color: #FFFFFF /*palegoldenrod*/; + border-width: 2px; + border-color: darkkhaki; + border-style: solid; + border-radius: 5; + padding: 3px; + font: 18px "黑体"; +} + +.QPushButton:pressed { + background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #dadbde, stop: 1 #f6f7fa); +} + +.MyButton { + background-color: #FFFFFF /*palegoldenrod*/; + border-width: 2px; + border-color: darkkhaki; + border-style: solid; + border-radius: 5; + padding: 3px; + font: 18px "黑体"; +} + +.MyButton:pressed { + background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #dadbde, stop: 1 #f6f7fa); +} + +.QGroupBox { + font: 15px "黑体"; +} + +.Chart { + font :15px "黑体"; +} + +.QLabel { + font: 18px "黑体"; +} + +.QLabel[state="0"] { + border-image:url(:/img/success_rect.png); +} + +.QLabel[state="1"] { + border-image:url(:/img/fail_rect.png); +} + +.QLabel[state="2"] { + border-image:url(:/img/warning_rect.png); +} + +.QLabel[state="3"] { + border-image:url(:/img/rect_gray.png); +} + + +.QComboBox { + background-color: #FFFFFF; + font: 18px "黑体"; +} + +.QCheckBox{ + width: 25px; + height: 25px; +} + +.QCheckBox::indicator { + width: 15px; + height: 15px; +} + +.QCheckBox::indicator[state="0"] { + image:url(:/img/normal.png); +} + +.QCheckBox::indicator[state="1"] { + image:url(:/img/fail.png); +} + + diff --git a/App/ToolsUI/Payload/Payload.ui b/App/ToolsUI/Payload/Payload.ui new file mode 100644 index 0000000..ef809f7 --- /dev/null +++ b/App/ToolsUI/Payload/Payload.ui @@ -0,0 +1,921 @@ + + + Payload + + + + 0 + 0 + 813 + 758 + + + + Form + + + + + + + + 待发指令 + + + + + + + true + + + + + + + + 100 + 22 + + + + + 16777215 + 150 + + + + background-color: rgb(85, 255, 127); + + + 发送 + + + + + + + + 100 + 22 + + + + + 16777215 + 150 + + + + background-color: rgb(255, 140, 142); + + + 取消 + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + 载荷供电指令 + + + + 12 + + + + + 500瓦打开 + + + + + + + 500瓦电源 + + + + + + + 500瓦关闭 + + + + + + + + + + 雷达告警设备 + + + + + + + + 255 + + + 1 + + + + + + + 雷达控制指令-开始 + + + + + + + 雷达控制指令-停止 + + + + + + + + + + + 威胁等级设置 + + + + + + + + + + 上传威胁等级 + + + + + + + + + + + 自动逃逸机动关闭 + + + + + + + 自动逃逸机动开启 + + + + + + + 强制开始机动 + + + + + + + 强制退出机动 + + + + + + + + + + + + + + 数据转发端口 + + + + + + + 数据转发IP地址 + + + + + + + 输出关闭 + + + + + + + 输出开启 + + + + + + + + + + + + + + + + + 注入雷达告警信号 + + + + + + + 取消注入雷达告警信号 + + + + + + + + + 受到威胁 + + + Qt::AlignCenter + + + + + + + + + + 紫外告警设备 + + + + + + + + 数据转发IP地址 + + + + + + + 输出开启 + + + + + + + 输出关闭 + + + + + + + 数据转发端口 + + + + + + + + + + + + + + + + + + + + 上电 + + + + + + + 下电 + + + + + + + + + + + 自动逃逸机动关闭 + + + + + + + 自动逃逸机动开启 + + + + + + + 取消注入紫外告警信号 + + + + + + + 注入紫外告警信号 + + + + + + + 强制开始机动 + + + + + + + 强制退出机动 + + + + + + + + + 受到威胁 + + + Qt::AlignCenter + + + + + + + + + + + + 干扰弹 + + + + + + + + 状态信息 + + + + + + + + 设备ID + + + + + + + background-color: rgb(189, 189, 189); + + + + + + Qt::AlignCenter + + + + + + + 工作状态 + + + + + + + background-color: rgb(189, 189, 189); + + + + + + Qt::AlignCenter + + + + + + + 投放开关 + + + + + + + background-color: rgb(189, 189, 189); + + + + + + Qt::AlignCenter + + + + + + + 电源电压 + + + + + + + background-color: rgb(189, 189, 189); + + + + + + Qt::AlignCenter + + + + + + + 火工开关 + + + + + + + background-color: rgb(189, 189, 189); + + + + + + Qt::AlignCenter + + + + + + + + + + + background-color: rgb(189, 189, 189); + + + 干扰弹1 + + + Qt::AlignCenter + + + + + + + background-color: rgb(189, 189, 189); + + + 通道1 + + + Qt::AlignCenter + + + + + + + background-color: rgb(189, 189, 189); + + + 干扰弹4 + + + Qt::AlignCenter + + + + + + + background-color: rgb(189, 189, 189); + + + 通道4 + + + Qt::AlignCenter + + + + + + + background-color: rgb(189, 189, 189); + + + 干扰弹2 + + + Qt::AlignCenter + + + + + + + background-color: rgb(189, 189, 189); + + + 通道2 + + + Qt::AlignCenter + + + + + + + background-color: rgb(189, 189, 189); + + + 干扰弹5 + + + Qt::AlignCenter + + + + + + + background-color: rgb(189, 189, 189); + + + 通道5 + + + Qt::AlignCenter + + + + + + + background-color: rgb(189, 189, 189); + + + 干扰弹3 + + + Qt::AlignCenter + + + + + + + background-color: rgb(189, 189, 189); + + + 通道3 + + + Qt::AlignCenter + + + + + + + background-color: rgb(189, 189, 189); + + + 干扰弹6 + + + Qt::AlignCenter + + + + + + + background-color: rgb(189, 189, 189); + + + 通道6 + + + Qt::AlignCenter + + + + + + + 左机翼 + + + Qt::AlignCenter + + + + + + + 右机翼 + + + Qt::AlignCenter + + + + + + + 自检状态 + + + + + + + background-color: rgb(189, 189, 189); + + + + + + Qt::AlignCenter + + + + + + + + + + + + 自动释放 + + + + + + + + 自动释放时间间隔(ms) + + + + + + + + + + 设置 + + + + + + + + + + + 告警后自动释放-开 + + + + + + + 告警后自动释放-关 + + + + + + + + + + + + 手动释放 + + + + + + + + 左机翼 + + + Qt::AlignCenter + + + + + + + 右机翼 + + + Qt::AlignCenter + + + + + + + 干扰弹1 + + + + + + + 干扰弹4 + + + + + + + 干扰弹2 + + + + + + + 干扰弹5 + + + + + + + 干扰弹3 + + + + + + + 干扰弹6 + + + + + + + + + + + 手动释放时间间隔(ms) + + + Qt::AlignCenter + + + + + + + + + + 设置 + + + + + + + 手动释放 + + + + + + + + + + + + + + + + 自检 + + + + + + + 保险开 + + + + + + + 保险关 + + + + + + + + + + + + + diff --git a/App/ToolsUI/Payload/dev/flare_device.cpp b/App/ToolsUI/Payload/dev/flare_device.cpp new file mode 100644 index 0000000..e82b051 --- /dev/null +++ b/App/ToolsUI/Payload/dev/flare_device.cpp @@ -0,0 +1,856 @@ +#include "flare_device.h" +#include +#include +#include +#include +#include + +namespace payload_protocol { + +// FlareUplinkData 实现 + +FlareUplinkData::FlareUplinkData() + : command_type(FlareCommandType::SAFETY_RELEASE) { + device_permissions.reserved_bit = 0; + device_permissions.device_permissions = 0; + command_data.fill(0, 5); +} + +QByteArray FlareUplinkData::serialize() const { + QByteArray data(FLARE_UPLINK_FRAME_LENGTH, 0); + + // 设置帧头 + data[0] = static_cast(FLARE_UPLINK_HEADER1); + data[1] = static_cast(FLARE_UPLINK_HEADER2); + + // 指令类型 + data[2] = static_cast(static_cast(command_type)); + + // 设备权限(16位,小端序) + quint16 permissions = (static_cast(device_permissions.reserved_bit) << 15) | + (device_permissions.device_permissions & 0x7FFF); + data[3] = static_cast(permissions & 0xFF); + data[4] = static_cast((permissions >> 8) & 0xFF); + + // 指令数据(5字节) + for (qint32 i = 0; i < command_data.size() && i < 5; ++i) { + data[5 + i] = command_data.at(i); + } + + // 计算双校验(前10字节参与校验计算) + quint8 additive_checksum = 0; + quint8 xor_checksum = 0; + + for (qint32 i = 0; i < FLARE_UPLINK_FRAME_LENGTH - 2; ++i) { + additive_checksum += static_cast(data.at(i)); + xor_checksum ^= static_cast(data.at(i)); + } + + data[10] = static_cast(additive_checksum); // 和校验 + data[11] = static_cast(xor_checksum); // 异或校验 + + return data; +} + +bool FlareUplinkData::deserialize(const QByteArray& data) { + if (data.size() < FLARE_UPLINK_FRAME_LENGTH) { + return false; + } + + // 验证帧头 + if (static_cast(data[0]) != FLARE_UPLINK_HEADER1 || + static_cast(data[1]) != FLARE_UPLINK_HEADER2) { + return false; + } + + // 验证校验和 + if (!validateChecksum(data)) { + return false; + } + + // 解析指令类型 + command_type = static_cast(static_cast(data[2])); + + // 解析设备权限(16位,小端序) + quint16 permissions = static_cast(static_cast(data[3])) | + (static_cast(static_cast(data[4])) << 8); + device_permissions.reserved_bit = (permissions >> 15) & 0x01; + device_permissions.device_permissions = permissions & 0x7FFF; + + // 解析指令数据 + command_data.resize(5); + for (qint32 i = 0; i < 5; ++i) { + if (5 + i < data.size() - 2) { // 排除校验字节 + command_data[i] = data[5 + i]; + } else { + command_data[i] = 0; + } + } + + return true; +} + +bool FlareUplinkData::validateChecksum(const QByteArray& data) const { + if (data.size() < FLARE_UPLINK_FRAME_LENGTH) { + return false; + } + + quint8 additive_checksum = 0; + quint8 xor_checksum = 0; + + // 前10字节参与校验计算 + for (qint32 i = 0; i < FLARE_UPLINK_FRAME_LENGTH - 2; ++i) { + additive_checksum += static_cast(data.at(i)); + xor_checksum ^= static_cast(data.at(i)); + } + + return (additive_checksum == static_cast(data.at(10))) && + (xor_checksum == static_cast(data.at(11))); +} + +void FlareUplinkData::setDevicePermissions(quint16 permissions) { + device_permissions.device_permissions = permissions & 0x7FFF; + device_permissions.reserved_bit = 0; // 保留位始终为0 +} + +bool FlareUplinkData::hasDevicePermission(quint8 device_id) const { + if (device_id < 1 || device_id > MAX_DEVICE_ID) { + return false; + } + quint16 mask = 1 << (device_id - 1); // 设备ID 1对应bit0,设备ID 15对应bit14 + return (device_permissions.device_permissions & mask) != 0; +} + +void FlareUplinkData::addDevicePermission(quint8 device_id) { + if (device_id >= 1 && device_id <= MAX_DEVICE_ID) { + quint16 mask = 1 << (device_id - 1); + device_permissions.device_permissions |= mask; + } +} + +void FlareUplinkData::removeDevicePermission(quint8 device_id) { + if (device_id >= 1 && device_id <= MAX_DEVICE_ID) { + quint16 mask = 1 << (device_id - 1); + device_permissions.device_permissions &= ~mask; + } +} + +quint16 FlareUplinkData::getDevicePermissions() const { + return device_permissions.device_permissions; +} + +// SafetyReleaseCommand 实现 + +SafetyReleaseCommand::SafetyReleaseCommand() + : safety_status(SAFETY_OFF) { + command_type = FlareCommandType::SAFETY_RELEASE; + command_data.fill(0, 5); + command_data[0] = static_cast(safety_status); +} + +void SafetyReleaseCommand::setSafetyStatus(bool on) { + safety_status = on ? SAFETY_ON : SAFETY_OFF; + command_data[0] = static_cast(safety_status); +} + +bool SafetyReleaseCommand::isSafetyOn() const { + return safety_status == SAFETY_ON; +} + +bool SafetyReleaseCommand::isSafetyOff() const { + return safety_status == SAFETY_OFF; +} + +QByteArray SafetyReleaseCommand::serialize() const { + FlareUplinkData base_data = *this; + return base_data.serialize(); +} + +bool SafetyReleaseCommand::deserialize(const QByteArray& data) { + if (!FlareUplinkData::deserialize(data)) { + return false; + } + + if (command_data.size() >= 1) { + safety_status = static_cast(command_data[0]); + } else { + safety_status = SAFETY_OFF; + } + + return true; +} + +// ManualTriggerCommand 实现 + +ManualTriggerCommand::ManualTriggerCommand() + : release_interval(0), channel_selection(0) { + command_type = FlareCommandType::MANUAL_TRIGGER; + command_data.fill(0, 5); + + // 设置默认参数 + command_data[0] = 0; // 投放间隔低字节 + command_data[1] = 0; // 投放间隔高字节 + command_data[2] = 0; // 通道选择字节1 + command_data[3] = 0; // 通道选择字节2 + command_data[4] = 0; // 通道选择字节3 +} + +void ManualTriggerCommand::setReleaseInterval(quint16 interval_ms) { + release_interval = interval_ms; + command_data[0] = static_cast(release_interval & 0xFF); + command_data[1] = static_cast((release_interval >> 8) & 0xFF); +} + +void ManualTriggerCommand::setChannelSelection(quint32 channel_mask) { + channel_selection = channel_mask & 0x00FFFFFF; // 24位掩码 + command_data[2] = static_cast(channel_selection & 0xFF); + command_data[3] = static_cast((channel_selection >> 8) & 0xFF); + command_data[4] = static_cast((channel_selection >> 16) & 0xFF); +} + +void ManualTriggerCommand::selectChannel(quint8 channel) { + if (channel >= 1 && channel <= MAX_FLARE_CHANNELS) { + channel_selection |= (1 << (channel - 1)); + setChannelSelection(channel_selection); + } +} + +void ManualTriggerCommand::deselectChannel(quint8 channel) { + if (channel >= 1 && channel <= MAX_FLARE_CHANNELS) { + channel_selection &= ~(1 << (channel - 1)); + setChannelSelection(channel_selection); + } +} + +bool ManualTriggerCommand::isChannelSelected(quint8 channel) const { + if (channel < 1 || channel > MAX_FLARE_CHANNELS) { + return false; + } + return (channel_selection & (1 << (channel - 1))) != 0; +} + +quint8 ManualTriggerCommand::getSelectedChannelCount() const { + quint32 mask = channel_selection; + quint8 count = 0; + while (mask) { + count += (mask & 1); + mask >>= 1; + } + return count; +} + +QByteArray ManualTriggerCommand::serialize() const { + FlareUplinkData base_data = *this; + return base_data.serialize(); +} + +bool ManualTriggerCommand::deserialize(const QByteArray& data) { + if (!FlareUplinkData::deserialize(data)) { + return false; + } + + // 解析投放间隔时间(2字节,小端序) + if (command_data.size() >= 2) { + release_interval = static_cast(static_cast(command_data[0])) | + (static_cast(static_cast(command_data[1])) << 8); + } else { + release_interval = 0; + } + + // 解析通道选择(3字节,小端序) + if (command_data.size() >= 5) { + channel_selection = static_cast(static_cast(command_data[2])) | + (static_cast(static_cast(command_data[3])) << 8) | + (static_cast(static_cast(command_data[4])) << 16); + } else { + channel_selection = 0; + } + + return true; +} + +// SelfTestCommand 实现 + +SelfTestCommand::SelfTestCommand() { + command_type = FlareCommandType::SELF_TEST; + command_data.fill(0, 5); +} + +QByteArray SelfTestCommand::serialize() const { + FlareUplinkData base_data = *this; + return base_data.serialize(); +} + +bool SelfTestCommand::deserialize(const QByteArray& data) { + return FlareUplinkData::deserialize(data); +} + +// FlareDownlinkData 实现 + +FlareDownlinkData::FlareDownlinkData() + : message_type(FlareDownlinkType::STATUS_AUTO), + current_device_id(1), + hardware_channel_count(24), + work_status(FlareWorkStatus::STANDBY), + mission_status(0), + self_test_status(SelfTestStatus::NOT_TESTED), + flare_status(0x00FFFFFF), // 默认所有弹正常 + channel_loaded_status(0x00FFFFFF), // 默认所有通道有弹 + response_frame_number(0) { + status_byte.reserved_bits = 0; + status_byte.safety_status = SafetyStatus::OFF; + status_byte.power_status = PowerVoltageStatus::NORMAL; + status_byte.igniter_status = IgniterStatus::OFF; +} + +QByteArray FlareDownlinkData::serialize() const { + QByteArray data(FLARE_DOWNLINK_FRAME_LENGTH, 0); + + // 设置帧头 + data[0] = static_cast(FLARE_DOWNLINK_HEADER1); + data[1] = static_cast(FLARE_DOWNLINK_HEADER2); + + // 报文类型 + data[2] = static_cast(static_cast(message_type)); + + // 当前设备ID + data[3] = static_cast(current_device_id); + + // 设备硬件通道数量 + data[4] = static_cast(hardware_channel_count); + + // 工作状态 + data[5] = static_cast(static_cast(work_status)); + + // 状态字节 + data[6] = static_cast((status_byte.reserved_bits << 5) | + (static_cast(status_byte.safety_status) << 4) | + (static_cast(status_byte.power_status) << 2) | + (static_cast(status_byte.igniter_status))); + + // 任务状态(2字节,小端序) + data[7] = static_cast(mission_status & 0xFF); + data[8] = static_cast((mission_status >> 8) & 0xFF); + + // 自检状态 + data[9] = static_cast(static_cast(self_test_status)); + + // 弹状态(3字节,小端序) + data[10] = static_cast(flare_status & 0xFF); + data[11] = static_cast((flare_status >> 8) & 0xFF); + data[12] = static_cast((flare_status >> 16) & 0xFF); + + // 通道有弹状态(3字节,小端序) + data[13] = static_cast(channel_loaded_status & 0xFF); + data[14] = static_cast((channel_loaded_status >> 8) & 0xFF); + data[15] = static_cast((channel_loaded_status >> 16) & 0xFF); + + // 响应帧序号(2字节,小端序) + data[16] = static_cast(response_frame_number & 0xFF); + data[17] = static_cast((response_frame_number >> 8) & 0xFF); + + // 计算双校验(前18字节参与校验计算) + quint8 additive_checksum = 0; + quint8 xor_checksum = 0; + + for (qint32 i = 0; i < FLARE_DOWNLINK_FRAME_LENGTH - 2; ++i) { + additive_checksum += static_cast(data.at(i)); + xor_checksum ^= static_cast(data.at(i)); + } + + data[18] = static_cast(additive_checksum); // 和校验 + data[19] = static_cast(xor_checksum); // 异或校验 + + return data; +} + +bool FlareDownlinkData::deserialize(const QByteArray& data) { + if (data.size() < FLARE_DOWNLINK_FRAME_LENGTH) { + return false; + } + + // 验证帧头 + if (static_cast(data[0]) != FLARE_DOWNLINK_HEADER1 || + static_cast(data[1]) != FLARE_DOWNLINK_HEADER2) { + return false; + } + + // 验证校验和 + if (!validateChecksum(data)) { + return false; + } + + // 解析报文类型 + message_type = static_cast(static_cast(data[2])); + + // 解析当前设备ID + current_device_id = static_cast(data[3]); + + // 解析设备硬件通道数量 + hardware_channel_count = static_cast(data[4]); + + // 解析工作状态 + work_status = static_cast(static_cast(data[5])); + + // 解析状态字节 + quint8 status_byte_value = static_cast(data[6]); + status_byte.reserved_bits = (status_byte_value >> 5) & 0x07; + status_byte.safety_status = static_cast((status_byte_value >> 4) & 0x01); + status_byte.power_status = static_cast((status_byte_value >> 2) & 0x03); + status_byte.igniter_status = static_cast(status_byte_value & 0x03); + + // 解析任务状态(2字节,小端序) + mission_status = static_cast(static_cast(data[7])) | + (static_cast(static_cast(data[8])) << 8); + + // 解析自检状态 + self_test_status = static_cast(static_cast(data[9])); + + // 解析弹状态(3字节,小端序) + flare_status = static_cast(static_cast(data[10])) | + (static_cast(static_cast(data[11])) << 8) | + (static_cast(static_cast(data[12])) << 16); + + // 解析通道有弹状态(3字节,小端序) + channel_loaded_status = static_cast(static_cast(data[13])) | + (static_cast(static_cast(data[14])) << 8) | + (static_cast(static_cast(data[15])) << 16); + + // 解析响应帧序号(2字节,小端序) + response_frame_number = static_cast(static_cast(data[16])) | + (static_cast(static_cast(data[17])) << 8); + + return true; +} + +bool FlareDownlinkData::validateChecksum(const QByteArray& data) const { + if (data.size() < FLARE_DOWNLINK_FRAME_LENGTH) { + return false; + } + + quint8 additive_checksum = 0; + quint8 xor_checksum = 0; + + // 前18字节参与校验计算 + for (qint32 i = 0; i < FLARE_DOWNLINK_FRAME_LENGTH - 2; ++i) { + additive_checksum += static_cast(data.at(i)); + xor_checksum ^= static_cast(data.at(i)); + } + + return (additive_checksum == static_cast(data.at(18))) && + (xor_checksum == static_cast(data.at(19))); +} + +bool FlareDownlinkData::isMissionExecuted(quint8 mission_id) const { + if (mission_id < 1 || mission_id > 15) { + return false; + } + quint16 mask = 1 << (mission_id - 1); // 任务1对应bit0,任务15对应bit14 + return (mission_status & mask) != 0; +} + +void FlareDownlinkData::setMissionExecuted(quint8 mission_id, bool executed) { + if (mission_id >= 1 && mission_id <= 15) { + quint16 mask = 1 << (mission_id - 1); + if (executed) { + mission_status |= mask; + } else { + mission_status &= ~mask; + } + } +} + +bool FlareDownlinkData::isFlareNormal(quint8 flare_id) const { + if (flare_id < 1 || flare_id > MAX_FLARE_CHANNELS) { + return false; + } + quint32 mask = 1 << (flare_id - 1); // 弹1对应bit0,弹24对应bit23 + return (flare_status & mask) != 0; +} + +void FlareDownlinkData::setFlareStatus(quint8 flare_id, bool normal) { + if (flare_id >= 1 && flare_id <= MAX_FLARE_CHANNELS) { + quint32 mask = 1 << (flare_id - 1); + if (normal) { + flare_status |= mask; + } else { + flare_status &= ~mask; + } + } +} + +bool FlareDownlinkData::isChannelLoaded(quint8 channel_id) const { + if (channel_id < 1 || channel_id > MAX_FLARE_CHANNELS) { + return false; + } + quint32 mask = 1 << (channel_id - 1); // 通道1对应bit0,通道24对应bit23 + return (channel_loaded_status & mask) != 0; +} + +void FlareDownlinkData::setChannelLoaded(quint8 channel_id, bool loaded) { + if (channel_id >= 1 && channel_id <= MAX_FLARE_CHANNELS) { + quint32 mask = 1 << (channel_id - 1); + if (loaded) { + channel_loaded_status |= mask; + } else { + channel_loaded_status &= ~mask; + } + } +} + +QString FlareDownlinkData::getWorkStatusDescription() const { + switch (work_status) { + case FlareWorkStatus::STANDBY: return QStringLiteral("待机"); + case FlareWorkStatus::MISSION_1: return QStringLiteral("正在执行任务1"); + case FlareWorkStatus::MISSION_2: return QStringLiteral("正在执行任务2"); + case FlareWorkStatus::MISSION_3: return QStringLiteral("正在执行任务3"); + case FlareWorkStatus::MISSION_4: return QStringLiteral("正在执行任务4"); + case FlareWorkStatus::MISSION_5: return QStringLiteral("正在执行任务5"); + case FlareWorkStatus::MISSION_6: return QStringLiteral("正在执行任务6"); + case FlareWorkStatus::MISSION_7: return QStringLiteral("正在执行任务7"); + case FlareWorkStatus::MISSION_8: return QStringLiteral("正在执行任务8"); + case FlareWorkStatus::MISSION_9: return QStringLiteral("正在执行任务9"); + case FlareWorkStatus::MISSION_10: return QStringLiteral("正在执行任务10"); + case FlareWorkStatus::MISSION_11: return QStringLiteral("正在执行任务11"); + case FlareWorkStatus::MISSION_12: return QStringLiteral("正在执行任务12"); + case FlareWorkStatus::MISSION_13: return QStringLiteral("正在执行任务13"); + case FlareWorkStatus::MISSION_14: return QStringLiteral("正在执行任务14"); + case FlareWorkStatus::MISSION_15: return QStringLiteral("正在执行任务15"); + case FlareWorkStatus::SELF_TESTING: return QStringLiteral("正在执行设备自检"); + case FlareWorkStatus::MANUAL_TRIGGERING: return QStringLiteral("正在执行手动触发"); + default: return QStringLiteral("未知状态"); + } +} + +QString FlareDownlinkData::getStatusSummary() const { + QString summary; + summary += QStringLiteral("设备ID: %1\n").arg(current_device_id); + summary += QStringLiteral("硬件通道数: %1\n").arg(hardware_channel_count); + summary += QStringLiteral("工作状态: %1\n").arg(getWorkStatusDescription()); + summary += QStringLiteral("保险状态: %1\n").arg(status_byte.safety_status == SafetyStatus::ON ? "开" : "关"); + summary += QStringLiteral("电源状态: %1\n").arg(status_byte.power_status == PowerVoltageStatus::NORMAL ? "正常" : "异常"); + summary += QStringLiteral("火工状态: "); + switch (status_byte.igniter_status) { + case IgniterStatus::OFF: summary += QStringLiteral("关"); break; + case IgniterStatus::ABNORMAL: summary += QStringLiteral("异常"); break; + case IgniterStatus::ON: summary += QStringLiteral("开"); break; + default: summary += QStringLiteral("未知"); break; + } + summary += QStringLiteral("\n"); + summary += QStringLiteral("自检状态: "); + switch (self_test_status) { + case SelfTestStatus::NOT_TESTED: summary += QStringLiteral("未自检"); break; + case SelfTestStatus::NORMAL: summary += QStringLiteral("正常"); break; + case SelfTestStatus::ABNORMAL: summary += QStringLiteral("异常"); break; + default: summary += QStringLiteral("未知"); break; + } + summary += QStringLiteral("\n"); + summary += QStringLiteral("响应帧序号: %1").arg(response_frame_number); + return summary; +} + +// FlareDevice 实现 + +FlareDevice::FlareDevice(QObject* parent) + : QObject(parent), device_id_(1) { + // 默认设备ID为1 + current_safety_status_ = false; + selected_channels_ = 0; + + // Register meta types for Qt signal/slot system + qRegisterMetaType(); +} + +FlareDevice::FlareDevice(quint8 device_id, QObject* parent) + : QObject(parent), device_id_(device_id) { + if (device_id < 1 || device_id > MAX_DEVICE_ID) { + device_id_ = 1; // 无效设备ID时设为1 + } + current_safety_status_ = false; + selected_channels_ = 0; + + // Register meta types for Qt signal/slot system + qRegisterMetaType(); +} + +QByteArray FlareDevice::createSafetyReleaseCommand(quint16 device_permissions, bool safety_on) const { + SafetyReleaseCommand command; + command.setDevicePermissions(device_permissions); + command.setSafetyStatus(safety_on); + return command.serialize(); +} + +QByteArray FlareDevice::createManualTriggerCommand(quint16 device_permissions, + quint16 release_interval, + quint32 channel_selection) const { + ManualTriggerCommand command; + command.setDevicePermissions(device_permissions); + command.setReleaseInterval(release_interval); + command.setChannelSelection(channel_selection); + return command.serialize(); +} + +QByteArray FlareDevice::createSelfTestCommand(quint16 device_permissions) const { + SelfTestCommand command; + command.setDevicePermissions(device_permissions); + return command.serialize(); +} + +QByteArray FlareDevice::createMissionTriggerCommand(quint16 device_permissions, quint8 mission_id) const { + FlareUplinkData command; + command.command_type = FlareCommandType::MISSION_TRIGGER; + command.setDevicePermissions(device_permissions); + + // 任务触发指令的数据格式:任务ID在第一个字节 + command.command_data.fill(0, 5); + if (mission_id >= 1 && mission_id <= 15) { + command.command_data[0] = static_cast(mission_id); + } + + return command.serialize(); +} + +QByteArray FlareDevice::createStatusQueryCommand(quint16 device_permissions) const { + FlareUplinkData command; + command.command_type = FlareCommandType::STATUS_QUERY; + command.setDevicePermissions(device_permissions); + command.command_data.fill(0, 5); + return command.serialize(); +} + +bool FlareDevice::parseStatusData(const QByteArray& data) { + FlareDownlinkData downlink_data; + if (!downlink_data.deserialize(data)) { + emit errorOccurred(QStringLiteral("无法解析干扰弹设备状态数据")); + return false; + } + + // 保存最后状态并处理变化 + handleStatusUpdate(downlink_data); + + return true; +} + +bool FlareDevice::parseStatusData(const QByteArray& data, FlareDownlinkData& downlink_data) const { + return downlink_data.deserialize(data); +} + +void FlareDevice::sendSafetyReleaseCommand(quint16 device_permissions, bool safety_on) { + QByteArray command = createSafetyReleaseCommand(device_permissions, safety_on); + emit commandSent(command); + current_safety_status_ = safety_on; + qDebug() << QString("投放保险%1指令已发送").arg(safety_on ? "开启" : "关闭"); +} + +void FlareDevice::sendManualTriggerCommand(quint16 device_permissions, + quint16 release_interval, + quint32 channel_selection) { + QByteArray command = createManualTriggerCommand(device_permissions, release_interval, channel_selection); + emit commandSent(command); + qDebug() << QString("手动触发指令已发送,间隔时间:%1ms,选择通道:%2") + .arg(release_interval) + .arg(QString::number(channel_selection, 16).toUpper()); +} + +void FlareDevice::sendSelfTestCommand(quint16 device_permissions) { + QByteArray command = createSelfTestCommand(device_permissions); + emit commandSent(command); + qDebug() << "设备自检指令已发送"; +} + +void FlareDevice::sendRawCommand(const QByteArray& command_data) { + if (validateUplinkFrameHeader(command_data)) { + emit commandSent(command_data); + } else { + emit errorOccurred(QStringLiteral("无效的干扰弹设备指令格式")); + } +} + +bool FlareDevice::validateUplinkFrameHeader(const QByteArray& data) { + if (data.size() < 2) { + return false; + } + return (static_cast(data[0]) == FLARE_UPLINK_HEADER1) && + (static_cast(data[1]) == FLARE_UPLINK_HEADER2); +} + +bool FlareDevice::validateDownlinkFrameHeader(const QByteArray& data) { + if (data.size() < 2) { + return false; + } + return (static_cast(data[0]) == FLARE_DOWNLINK_HEADER1) && + (static_cast(data[1]) == FLARE_DOWNLINK_HEADER2); +} + +quint8 FlareDevice::calculateAdditiveChecksum(const QByteArray& data, qint32 length) { + if (length == 0) { + length = data.size(); + } + + quint8 checksum = 0; + for (qint32 i = 0; i < length && i < data.size(); ++i) { + checksum += static_cast(data.at(i)); + } + return checksum; +} + +quint8 FlareDevice::calculateXorChecksum(const QByteArray& data, qint32 length) { + if (length == 0) { + length = data.size(); + } + + quint8 checksum = 0; + for (qint32 i = 0; i < length && i < data.size(); ++i) { + checksum ^= static_cast(data.at(i)); + } + return checksum; +} + +quint16 FlareDevice::createDevicePermissions(quint8 device_id) { + if (device_id >= 1 && device_id <= MAX_DEVICE_ID) { + return 1 << (device_id - 1); + } + return 0; +} + +quint16 FlareDevice::createMultiDevicePermissions(const QList& device_ids) { + quint16 permissions = 0; + for (quint8 device_id : device_ids) { + if (device_id >= 1 && device_id <= MAX_DEVICE_ID) { + permissions |= (1 << (device_id - 1)); + } + } + return permissions; +} + +QList FlareDevice::extractDeviceIdsFromPermissions(quint16 permissions) { + QList device_ids; + for (quint8 i = 1; i <= MAX_DEVICE_ID; ++i) { + if (permissions & (1 << (i - 1))) { + device_ids.append(i); + } + } + return device_ids; +} + +quint32 FlareDevice::createChannelSelectionMask(const QList& channels) { + quint32 mask = 0; + for (quint8 channel : channels) { + if (channel >= 1 && channel <= MAX_FLARE_CHANNELS) { + mask |= (1 << (channel - 1)); + } + } + return mask; +} + +QList FlareDevice::extractChannelsFromMask(quint32 channel_mask) { + QList channels; + for (quint8 i = 1; i <= MAX_FLARE_CHANNELS; ++i) { + if (channel_mask & (1 << (i - 1))) { + channels.append(i); + } + } + return channels; +} + +bool FlareDevice::isValidChannel(quint8 channel_id) { + return channel_id >= 1 && channel_id <= MAX_FLARE_CHANNELS; +} + +bool FlareDevice::isValidDeviceId(quint8 device_id) { + return device_id >= 1 && device_id <= MAX_DEVICE_ID; +} + +void FlareDevice::setDevicePermissionsInCommand(QByteArray& command, quint16 permissions) const { + if (command.size() >= 5) { + // 设置权限字节(小端序) + command[3] = static_cast(permissions & 0xFF); + command[4] = static_cast((permissions >> 8) & 0xFF); + } +} + +QByteArray FlareDevice::createBaseCommandFrame(FlareCommandType command_type, + quint16 device_permissions, + const QByteArray& command_data) const { + FlareUplinkData base_command; + base_command.command_type = command_type; + base_command.setDevicePermissions(device_permissions); + + if (command_data.size() >= 5) { + for (qint32 i = 0; i < 5; ++i) { + base_command.command_data[i] = command_data.at(i); + } + } + + return base_command.serialize(); +} + +// 槽函数实现 + +void FlareDevice::setSafety(bool on) { + quint16 permissions = createDevicePermissions(device_id_); + sendSafetyReleaseCommand(permissions, on); +} + +void FlareDevice::selectChannel(quint8 channel) { + if (channel >= 1 && channel <= MAX_FLARE_CHANNELS) { + selected_channels_ |= (1 << (channel - 1)); + qDebug() << QString("通道 %1 已选择").arg(channel); + } else { + emit errorOccurred(QStringLiteral("无效的通道ID: %1").arg(channel)); + } +} + +void FlareDevice::deselectChannel(quint8 channel) { + if (channel >= 1 && channel <= MAX_FLARE_CHANNELS) { + selected_channels_ &= ~(1 << (channel - 1)); + qDebug() << QString("通道 %1 已取消选择").arg(channel); + } else { + emit errorOccurred(QStringLiteral("无效的通道ID: %1").arg(channel)); + } +} + +void FlareDevice::triggerManualRelease(quint16 release_interval, const QList& channels) { + quint32 channel_mask = createChannelSelectionMask(channels); + quint16 permissions = createDevicePermissions(device_id_); + sendManualTriggerCommand(permissions, release_interval, channel_mask); +} + +void FlareDevice::performSelfTest() { + quint16 permissions = createDevicePermissions(device_id_); + sendSelfTestCommand(permissions); +} + +void FlareDevice::requestStatusUpdate() { + quint16 permissions = createDevicePermissions(device_id_); + QByteArray command = createStatusQueryCommand(permissions); + emit commandSent(command); + qDebug() << "状态查询请求已发送"; +} + +void FlareDevice::resetDevice() { + current_safety_status_ = false; + selected_channels_ = 0; + qDebug() << "设备已重置"; + emit errorOccurred(QStringLiteral("设备已重置")); +} + +void FlareDevice::handleStatusUpdate(const payload_protocol::FlareDownlinkData& status) { + // 保存最后状态 + last_status_ = status; + + // 检查保险状态变化 + bool new_safety_status = (status.status_byte.safety_status == SafetyStatus::ON); + if (new_safety_status != current_safety_status_) { + current_safety_status_ = new_safety_status; + emit safetyStatusChanged(new_safety_status); + } + + // 发出状态更新信号 + emit statusUpdated(status); + + // 可以添加更多的状态变化检查,比如弹状态、通道状态等 + // 这里简单起见,只检查保险状态 +} + +} // namespace payload_protocol \ No newline at end of file diff --git a/App/ToolsUI/Payload/dev/flare_device.h b/App/ToolsUI/Payload/dev/flare_device.h new file mode 100644 index 0000000..983c7af --- /dev/null +++ b/App/ToolsUI/Payload/dev/flare_device.h @@ -0,0 +1,454 @@ +#ifndef FLARE_DEVICE_H +#define FLARE_DEVICE_H + +#include +#include +#include +#include +#include +#include +#include + +namespace payload_protocol { + +// 干扰弹设备指令类型 +enum class FlareCommandType : quint8 { + SAFETY_RELEASE = 0x01, // 投放保险指令 + MISSION_TRIGGER = 0x02, // 任务触发指令 + MANUAL_TRIGGER = 0x03, // 手动触发指令 + SELF_TEST = 0x04, // 设备自检指令 + STATUS_QUERY = 0x25 // 状态查询指令 +}; + +// 干扰弹下行报文类型 +enum class FlareDownlinkType : quint8 { + STATUS_AUTO = 0xE5 // 状态信息自动下传 +}; + +// 工作状态枚举 +enum class FlareWorkStatus : quint8 { + STANDBY = 0x00, // 待机 + MISSION_1 = 0x01, // 正在执行任务1 + MISSION_2 = 0x02, // 正在执行任务2 + MISSION_3 = 0x03, // 正在执行任务3 + MISSION_4 = 0x04, // 正在执行任务4 + MISSION_5 = 0x05, // 正在执行任务5 + MISSION_6 = 0x06, // 正在执行任务6 + MISSION_7 = 0x07, // 正在执行任务7 + MISSION_8 = 0x08, // 正在执行任务8 + MISSION_9 = 0x09, // 正在执行任务9 + MISSION_10 = 0x0A, // 正在执行任务10 + MISSION_11 = 0x0B, // 正在执行任务11 + MISSION_12 = 0x0C, // 正在执行任务12 + MISSION_13 = 0x0D, // 正在执行任务13 + MISSION_14 = 0x0E, // 正在执行任务14 + MISSION_15 = 0x0F, // 正在执行任务15 + SELF_TESTING = 0x40, // 正在执行设备自检 + MANUAL_TRIGGERING = 0xFF // 正在执行手动触发 +}; + +// 保险状态 +enum class SafetyStatus : quint8 { + OFF = 0x00, // 关 + ON = 0x01 // 开 +}; + +// 电源电压状态 +enum class PowerVoltageStatus : quint8 { + NORMAL = 0x00, // 电压正常 + ABNORMAL = 0x01 // 电压异常 +}; + +// 火工开关状态 +enum class IgniterStatus : quint8 { + OFF = 0x00, // 火工关 + ABNORMAL = 0x01, // 火工异常 + ON = 0x03 // 火工开 +}; + +// 自检状态 +enum class SelfTestStatus : quint8 { + NOT_TESTED = 0x00, // 上电未自检 + NORMAL = 0x01, // 自检正常 + ABNORMAL = 0x02 // 自检异常 +}; + +// 干扰弹帧头 +constexpr quint8 FLARE_UPLINK_HEADER1 = 0xE8; +constexpr quint8 FLARE_UPLINK_HEADER2 = 0xD7; +constexpr quint8 FLARE_DOWNLINK_HEADER1 = 0xE6; +constexpr quint8 FLARE_DOWNLINK_HEADER2 = 0xD5; + +// 干扰弹帧长度 +constexpr qint32 FLARE_UPLINK_FRAME_LENGTH = 12; // 上行帧长度 +constexpr qint32 FLARE_DOWNLINK_FRAME_LENGTH = 20; // 下行帧长度 + +// 最大通道数量 +constexpr qint32 MAX_FLARE_CHANNELS = 24; +constexpr qint32 MAX_DEVICE_ID = 15; + +// 投放保险指令参数 +constexpr quint8 SAFETY_ON = 0xAA; // 投放保险开 +constexpr quint8 SAFETY_OFF = 0x55; // 投放保险关 + +// 干扰弹上行指令基础结构 +struct FlareUplinkData { + // 字节0-1: 帧头(在序列化时添加) + + // 字节2: 指令类型 + FlareCommandType command_type; + + // 字节3-4: 权限/设备号(16位) + struct { + quint8 reserved_bit : 1; // bit15: 保留位,为0 + quint16 device_permissions : 15; // bit14~0: 对应设备ID为15~1的控制权限 + } device_permissions; + + // 字节5-9: 数据(具体内容取决于指令类型) + QByteArray command_data; + + // 字节10: 和校验(在序列化时计算) + // 字节11: 异或校验(在序列化时计算) + + FlareUplinkData(); + + // 序列化为字节流 + QByteArray serialize() const; + + // 从字节流反序列化 + bool deserialize(const QByteArray& data); + + // 验证双校验 + bool validateChecksum(const QByteArray& data) const; + + // 设置设备权限(支持多设备控制) + void setDevicePermissions(quint16 permissions); + + // 检查是否具有特定设备权限 + bool hasDevicePermission(quint8 device_id) const; + + // 添加设备权限 + void addDevicePermission(quint8 device_id); + + // 移除设备权限 + void removeDevicePermission(quint8 device_id); + + // 获取设备权限掩码 + quint16 getDevicePermissions() const; +}; + +// 投放保险指令数据结构 +struct SafetyReleaseCommand : public FlareUplinkData { + quint8 safety_status; // 投放保险状态:0xAA开,0x55关 + + SafetyReleaseCommand(); + + // 设置投放保险状态 + void setSafetyStatus(bool on); + + // 检查是否为开启命令 + bool isSafetyOn() const; + + // 检查是否为关闭命令 + bool isSafetyOff() const; + + // 序列化 + QByteArray serialize() const; + + // 反序列化 + bool deserialize(const QByteArray& data); +}; + +// 手动触发指令数据结构 +struct ManualTriggerCommand : public FlareUplinkData { + quint16 release_interval; // 投放间隔时间,单位ms,范围0-60000 + quint32 channel_selection; // 通道选择,24位掩码 + + ManualTriggerCommand(); + + // 设置投放间隔时间 + void setReleaseInterval(quint16 interval_ms); + + // 设置通道选择 + void setChannelSelection(quint32 channel_mask); + + // 选择特定通道 + void selectChannel(quint8 channel); + + // 取消选择通道 + void deselectChannel(quint8 channel); + + // 检查通道是否被选择 + bool isChannelSelected(quint8 channel) const; + + // 获取选择的通道数量 + quint8 getSelectedChannelCount() const; + + // 序列化 + QByteArray serialize() const; + + // 反序列化 + bool deserialize(const QByteArray& data); +}; + +// 设备自检指令数据结构 +struct SelfTestCommand : public FlareUplinkData { + SelfTestCommand(); + + // 序列化 + QByteArray serialize() const; + + // 反序列化 + bool deserialize(const QByteArray& data); +}; + +// 干扰弹下行状态数据结构 +struct FlareDownlinkData { + // 字节0-1: 帧头(在序列化时添加) + + // 字节2: 报文类型 + FlareDownlinkType message_type; + + // 字节3: 当前设备ID + quint8 current_device_id; + + // 字节4: 设备硬件通道数量 + quint8 hardware_channel_count; + + // 字节5: 工作状态 + FlareWorkStatus work_status; + + // 字节6: 电源电压、火工、保险状态 + struct { + quint8 reserved_bits : 3; // bit7~5: 保留 + SafetyStatus safety_status : 1; // bit4: 投放保险状态 + PowerVoltageStatus power_status : 2; // bit3~2: 电源电压状态 + IgniterStatus igniter_status : 2; // bit1~0: 火工开关状态 + } status_byte; + + // 字节7-8: 任务状态(16位,bit0~14对应任务1~15) + quint16 mission_status; + + // 字节9: 自检状态 + SelfTestStatus self_test_status; + + // 字节10-12: 弹状态(24位,bit0~23对应弹1~24) + quint32 flare_status; + + // 字节13-15: 通道有弹状态(24位,bit0~23对应通道1~24) + quint32 channel_loaded_status; + + // 字节16-17: 响应帧序号(16位) + quint16 response_frame_number; + + // 字节18: 和校验(在序列化时计算) + // 字节19: 异或校验(在序列化时计算) + + FlareDownlinkData(); + + // 序列化为字节流 + QByteArray serialize() const; + + // 从字节流反序列化 + bool deserialize(const QByteArray& data); + + // 验证双校验 + bool validateChecksum(const QByteArray& data) const; + + // 检查任务是否已执行 + bool isMissionExecuted(quint8 mission_id) const; + + // 设置任务执行状态 + void setMissionExecuted(quint8 mission_id, bool executed); + + // 检查弹是否正常 + bool isFlareNormal(quint8 flare_id) const; + + // 设置弹状态 + void setFlareStatus(quint8 flare_id, bool normal); + + // 检查通道是否有弹 + bool isChannelLoaded(quint8 channel_id) const; + + // 设置通道有弹状态 + void setChannelLoaded(quint8 channel_id, bool loaded); + + // 获取工作状态描述 + QString getWorkStatusDescription() const; + + // 获取状态摘要 + QString getStatusSummary() const; +}; + +// 干扰弹设备类 - 支持信号槽机制 +class FlareDevice : public QObject { + Q_OBJECT + +public: + explicit FlareDevice(QObject* parent = nullptr); + explicit FlareDevice(quint8 device_id, QObject* parent = nullptr); + + // 获取设备ID + quint8 getDeviceId() const { return device_id_; } + + // 设置设备ID + void setDeviceId(quint8 id) { device_id_ = id; } + + // 创建投放保险指令 + QByteArray createSafetyReleaseCommand(quint16 device_permissions, bool safety_on) const; + + // 创建手动触发指令 + QByteArray createManualTriggerCommand(quint16 device_permissions, + quint16 release_interval, + quint32 channel_selection) const; + + // 创建设备自检指令 + QByteArray createSelfTestCommand(quint16 device_permissions) const; + + // 创建任务触发指令(靶机不使用,但协议中有定义) + QByteArray createMissionTriggerCommand(quint16 device_permissions, quint8 mission_id) const; + + // 创建状态查询指令 + QByteArray createStatusQueryCommand(quint16 device_permissions) const; + + // 解析下行状态数据 - 槽函数 + Q_SLOT bool parseStatusData(const QByteArray& data); + + // 手动解析状态数据(兼容原有接口) + bool parseStatusData(const QByteArray& data, FlareDownlinkData& downlink_data) const; + + // 发送投放保险指令 - 槽函数 + Q_SLOT void sendSafetyReleaseCommand(quint16 device_permissions, bool safety_on); + + // 发送手动触发指令 - 槽函数 + Q_SLOT void sendManualTriggerCommand(quint16 device_permissions, + quint16 release_interval, + quint32 channel_selection); + + // 发送设备自检指令 - 槽函数 + Q_SLOT void sendSelfTestCommand(quint16 device_permissions); + + // 发送原始指令数据 - 槽函数 + Q_SLOT void sendRawCommand(const QByteArray& command_data); + + // 验证上行帧头 + static bool validateUplinkFrameHeader(const QByteArray& data); + + // 验证下行帧头 + static bool validateDownlinkFrameHeader(const QByteArray& data); + + // 计算和校验 + static quint8 calculateAdditiveChecksum(const QByteArray& data, qint32 length = 0); + + // 计算异或校验 + static quint8 calculateXorChecksum(const QByteArray& data, qint32 length = 0); + + // 创建设备权限掩码(支持多设备) + static quint16 createDevicePermissions(quint8 device_id); + + // 创建多设备权限掩码 + static quint16 createMultiDevicePermissions(const QList& device_ids); + + // 从权限掩码中提取设备ID列表 + static QList extractDeviceIdsFromPermissions(quint16 permissions); + + // 创建通道选择掩码 + static quint32 createChannelSelectionMask(const QList& channels); + + // 从通道掩码中提取通道列表 + static QList extractChannelsFromMask(quint32 channel_mask); + + // 检查通道ID是否有效(1-24) + static bool isValidChannel(quint8 channel_id); + + // 检查设备ID是否有效(1-15) + static bool isValidDeviceId(quint8 device_id); + +signals: + // 状态更新信号 + void statusUpdated(const payload_protocol::FlareDownlinkData& status); + + // 保险状态变化信号 + void safetyStatusChanged(bool safety_on); + + // 弹状态变化信号 + void flareStatusChanged(quint8 flare_id, bool normal); + + // 通道有弹状态变化信号 + void channelLoadedStatusChanged(quint8 channel_id, bool loaded); + + // 任务执行状态变化信号 + void missionStatusChanged(quint8 mission_id, bool executed); + + // 设备连接状态变化信号 + void connectionStatusChanged(bool connected); + + // 指令发送信号 + void commandSent(const QByteArray& command_data); + + // 错误信号 + void errorOccurred(const QString& error_message); + +public slots: + // 设置投放保险状态 + Q_SLOT void setSafety(bool on); + + // 选择特定通道 + Q_SLOT void selectChannel(quint8 channel); + + // 取消选择通道 + Q_SLOT void deselectChannel(quint8 channel); + + // 触发手动投放 + Q_SLOT void triggerManualRelease(quint16 release_interval, const QList& channels); + + // 执行设备自检 + Q_SLOT void performSelfTest(); + + // 请求状态更新 + Q_SLOT void requestStatusUpdate(); + + // 重置设备 + Q_SLOT void resetDevice(); + +private slots: + // 处理内部状态更新 + void handleStatusUpdate(const payload_protocol::FlareDownlinkData& status); + +private: + quint8 device_id_; // 当前设备ID(1-15) + + // 最后接收到的状态 + FlareDownlinkData last_status_; + + // 当前保险状态 + bool current_safety_status_ = false; + + // 当前选择的通道 + quint32 selected_channels_ = 0; + + // 设置设备权限到指令数据中 + void setDevicePermissionsInCommand(QByteArray& command, quint16 permissions) const; + + // 创建基础指令帧 + QByteArray createBaseCommandFrame(FlareCommandType command_type, + quint16 device_permissions, + const QByteArray& command_data) const; +}; + +} // namespace payload_protocol + +Q_DECLARE_METATYPE(payload_protocol::FlareCommandType) +Q_DECLARE_METATYPE(payload_protocol::FlareDownlinkType) +Q_DECLARE_METATYPE(payload_protocol::FlareWorkStatus) +Q_DECLARE_METATYPE(payload_protocol::SafetyStatus) +Q_DECLARE_METATYPE(payload_protocol::PowerVoltageStatus) +Q_DECLARE_METATYPE(payload_protocol::IgniterStatus) +Q_DECLARE_METATYPE(payload_protocol::SelfTestStatus) +Q_DECLARE_METATYPE(payload_protocol::FlareDownlinkData) +Q_DECLARE_METATYPE(payload_protocol::FlareUplinkData) +Q_DECLARE_METATYPE(payload_protocol::SafetyReleaseCommand) +Q_DECLARE_METATYPE(payload_protocol::ManualTriggerCommand) +Q_DECLARE_METATYPE(payload_protocol::SelfTestCommand) + +#endif // FLARE_DEVICE_H \ No newline at end of file diff --git a/App/ToolsUI/Payload/dev/payload_protocol.h b/App/ToolsUI/Payload/dev/payload_protocol.h new file mode 100644 index 0000000..877754d --- /dev/null +++ b/App/ToolsUI/Payload/dev/payload_protocol.h @@ -0,0 +1,144 @@ + +#ifndef PAYLOAD_PROTOCOL_H +#define PAYLOAD_PROTOCOL_H + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace payload_protocol { + +// 设备类型枚举 +enum class DeviceType : quint8 { + UV_WARNING = 0x02, // 紫外告警设备 + RADAR_WARNING = 0x03, // 雷达告警设备 (协议中未明确给出,假设为0x03) + JAMMER = 0x04 // 干扰弹设备 (协议中未明确给出,假设为0x04) +}; + +// 通信方向 +enum class Direction { + UPLINK, // 上行:地面站 -> 靶机 -> 载荷 + DOWNLINK // 下行:载荷 -> 靶机 -> 地面站 +}; + +// 校验类型 +enum class ChecksumType { + ADDITIVE, // 累加和校验(紫外、雷达告警) + DUAL_ADD_XOR // 双校验:累加和+异或(干扰弹) +}; + +// 协议异常类 - 使用 QString +class ProtocolException : public std::runtime_error { +public: + explicit ProtocolException(const QString& message) + : std::runtime_error(message.toStdString()) {} +}; + +// 基础帧结构 - 使用 QByteArray +struct BaseFrame { + virtual ~BaseFrame() = default; + virtual QByteArray serialize() const = 0; + virtual bool deserialize(const QByteArray& data) = 0; + virtual bool validateChecksum(const QByteArray& data) const = 0; +}; + +// 校验计算工具类 +class ChecksumCalculator { +public: + // 计算累加和校验(取低8位) + static quint8 calculateAdditiveChecksum(const QByteArray& data, + qint32 start = 0, + qint32 length = 0) { + if (length == 0) length = data.size(); + quint16 sum = 0; + for (qint32 i = start; i < start + length && i < data.size(); ++i) { + sum += static_cast(data.at(i)); + } + return static_cast(sum & 0xFF); + } + + // 计算异或校验 + static quint8 calculateXorChecksum(const QByteArray& data, + qint32 start = 0, + qint32 length = 0) { + if (length == 0) length = data.size(); + quint8 xor_value = 0; + for (qint32 i = start; i < start + length && i < data.size(); ++i) { + xor_value ^= static_cast(data.at(i)); + } + return xor_value; + } + + // 验证累加和校验 + static bool verifyAdditiveChecksum(const QByteArray& data, + qint32 checksum_pos) { + if (checksum_pos >= data.size()) return false; + quint8 calculated = calculateAdditiveChecksum(data, 0, checksum_pos); + return calculated == static_cast(data.at(checksum_pos)); + } + + // 验证双校验(累加和+异或) + static bool verifyDualChecksum(const QByteArray& data, + qint32 additive_pos, + qint32 xor_pos) { + if (additive_pos >= data.size() || xor_pos >= data.size()) return false; + + quint8 calc_additive = calculateAdditiveChecksum(data, 0, additive_pos); + quint8 calc_xor = calculateXorChecksum(data, 0, additive_pos); + + return (calc_additive == static_cast(data.at(additive_pos))) && + (calc_xor == static_cast(data.at(xor_pos))); + } +}; + +// 帧头常量定义 +namespace FrameHeaders { + // 紫外告警帧头 + constexpr quint8 UV_HEADER1 = 0xDD; + constexpr quint8 UV_HEADER2 = 0x77; + + // 雷达告警帧头 (EB 90,注意字节序) + constexpr quint8 RADAR_HEADER1 = 0xEB; + constexpr quint8 RADAR_HEADER2 = 0x90; + + // 干扰弹上行帧头 + constexpr quint8 JAMMER_UPLINK_HEADER1 = 0xE8; + constexpr quint8 JAMMER_UPLINK_HEADER2 = 0xD7; + + // 干扰弹下行帧头 + constexpr quint8 JAMMER_DOWNLINK_HEADER1 = 0xE6; + constexpr quint8 JAMMER_DOWNLINK_HEADER2 = 0xD5; +} + +// 协议版本信息 +struct ProtocolVersion { + quint8 major; + quint8 minor; + quint16 year; + quint8 month; + + static ProtocolVersion current() { + return {1, 0, 2025, 12}; // 基于文档日期 2025年12月 + } +}; + +// 接口要求结构 - 使用 QList 替代 std::vector +struct InterfaceRequirements { + quint8 serial_port; // 串口号 + quint32 baud_rate; // 波特率 + QList pins; // 使用针脚 + + InterfaceRequirements() : serial_port(0), baud_rate(0) {} + + InterfaceRequirements(quint8 port, quint32 baud, const QList& pin_list) + : serial_port(port), baud_rate(baud), pins(pin_list) {} +}; + +} // namespace payload_protocol + +#endif // PAYLOAD_PROTOCOL_H \ No newline at end of file diff --git a/App/ToolsUI/Payload/dev/radar_warning_device.cpp b/App/ToolsUI/Payload/dev/radar_warning_device.cpp new file mode 100644 index 0000000..2d77cc4 --- /dev/null +++ b/App/ToolsUI/Payload/dev/radar_warning_device.cpp @@ -0,0 +1,504 @@ +#include "radar_warning_device.h" +#include +#include +#include + +namespace payload_protocol { + +// RadarDownlinkData 实现 + +RadarDownlinkData::RadarDownlinkData() + : message_type(RadarMessageType::HEARTBEAT), threat_level(ThreatLevel::LEVEL_0) { + device_info.device_id = 0; + device_info.reserved_bit = 0; + reserved_bytes1.fill(0, 11); + reserved_bytes2.fill(0, 13); +} + +QByteArray RadarDownlinkData::serialize() const { + QByteArray data(RADAR_DOWNLINK_FRAME_LENGTH, 0); + + // 设置帧头(注意字节序:协议示例显示为90 EB,可能是小端序) + data[0] = static_cast(RADAR_HEADER2); // 0x90 + data[1] = static_cast(RADAR_HEADER1); // 0xEB + + // 设备号 + 保留位 + data[2] = static_cast((device_info.device_id & 0x7F) | ((device_info.reserved_bit & 0x01) << 7)); + + // 报文类型 + data[3] = static_cast(static_cast(message_type)); + + // 保留字节1(11字节) + for (qint32 i = 0; i < reserved_bytes1.size() && i < 11; ++i) { + data[4 + i] = reserved_bytes1.at(i); + } + + // 威胁等级 + data[15] = static_cast(static_cast(threat_level)); + + // 保留字节2(13字节) + for (qint32 i = 0; i < reserved_bytes2.size() && i < 13; ++i) { + data[16 + i] = reserved_bytes2.at(i); + } + + // 计算校验和(从第3个字节开始计算,到第29个字节结束,共27个字节) + quint8 checksum = 0; + for (qint32 i = 2; i < RADAR_DOWNLINK_FRAME_LENGTH - 1; ++i) { + checksum += static_cast(data.at(i)); + } + data[RADAR_DOWNLINK_FRAME_LENGTH - 1] = static_cast(checksum); + + return data; +} + +bool RadarDownlinkData::deserialize(const QByteArray& data) { + if (data.size() < RADAR_DOWNLINK_FRAME_LENGTH) { + return false; + } + + // 验证帧头(注意字节序) + if (static_cast(data[0]) != RADAR_HEADER2 || static_cast(data[1]) != RADAR_HEADER1) { + return false; + } + + // 验证校验和 + if (!validateChecksum(data)) { + return false; + } + + // 解析设备号 + 保留位 + quint8 byte2 = static_cast(data[2]); + device_info.device_id = byte2 & 0x7F; + device_info.reserved_bit = (byte2 >> 7) & 0x01; + + // 解析报文类型 + message_type = static_cast(static_cast(data[3])); + + // 解析保留字节1 + reserved_bytes1.resize(11); + for (qint32 i = 0; i < 11; ++i) { + if (4 + i < data.size() - 1) { // 排除校验和字节 + reserved_bytes1[i] = data[4 + i]; + } else { + reserved_bytes1[i] = 0; + } + } + + // 解析威胁等级 + quint8 threat_value = static_cast(data[15]); + if (threat_value <= 5) { + threat_level = static_cast(threat_value); + } else { + threat_level = ThreatLevel::LEVEL_0; + } + + // 解析保留字节2 + reserved_bytes2.resize(13); + for (qint32 i = 0; i < 13; ++i) { + if (16 + i < data.size() - 1) { // 排除校验和字节 + reserved_bytes2[i] = data[16 + i]; + } else { + reserved_bytes2[i] = 0; + } + } + + return true; +} + +bool RadarDownlinkData::validateChecksum(const QByteArray& data) const { + if (data.size() < RADAR_DOWNLINK_FRAME_LENGTH) { + return false; + } + + quint8 checksum = 0; + // 从第3个字节开始计算(跳过帧头) + for (qint32 i = 2; i < RADAR_DOWNLINK_FRAME_LENGTH - 1; ++i) { + checksum += static_cast(data.at(i)); + } + + return checksum == static_cast(data.at(RADAR_DOWNLINK_FRAME_LENGTH - 1)); +} + +bool RadarDownlinkData::shouldTriggerAlarm(ThreatLevel set_threat_level) const { + return static_cast(threat_level) >= static_cast(set_threat_level); +} + +quint8 RadarDownlinkData::getThreatLevelValue() const { + return static_cast(threat_level); +} + +void RadarDownlinkData::setThreatLevel(ThreatLevel level) { + threat_level = level; +} + +void RadarDownlinkData::setDeviceId(quint8 id) { + device_info.device_id = id & 0x7F; // 只取低7位 +} + +quint8 RadarDownlinkData::getDeviceId() const { + return device_info.device_id; +} + +// RadarUplinkData 实现 + +RadarUplinkData::RadarUplinkData() + : device_id(0) { + control_info.message_type = 0x04; // 默认报文类型为04H + control_info.work_status = 0; // 默认停止状态 + reserved_bytes.fill(0, 8); +} + +QByteArray RadarUplinkData::serialize() const { + QByteArray data(RADAR_UPLINK_FRAME_LENGTH, 0); + + // 设置帧头(注意字节序) + data[0] = static_cast(RADAR_HEADER2); // 0x90 + data[1] = static_cast(RADAR_HEADER1); // 0xEB + + // 设备号 + data[2] = static_cast(device_id); + + // 控制信息:报文类型(低4位)+ 工作状态(高4位) + data[3] = static_cast((control_info.message_type & 0x0F) | ((control_info.work_status & 0x0F) << 4)); + + // 保留字节 + for (qint32 i = 0; i < reserved_bytes.size() && i < 8; ++i) { + data[4 + i] = reserved_bytes.at(i); + } + + // 计算校验和(从第3个字节开始计算) + quint8 checksum = 0; + for (qint32 i = 2; i < RADAR_UPLINK_FRAME_LENGTH - 1; ++i) { + checksum += static_cast(data.at(i)); + } + data[RADAR_UPLINK_FRAME_LENGTH - 1] = static_cast(checksum); + + return data; +} + +bool RadarUplinkData::deserialize(const QByteArray& data) { + if (data.size() < RADAR_UPLINK_FRAME_LENGTH) { + return false; + } + + // 验证帧头 + if (static_cast(data[0]) != RADAR_HEADER2 || static_cast(data[1]) != RADAR_HEADER1) { + return false; + } + + // 验证校验和 + if (!validateChecksum(data)) { + return false; + } + + // 解析设备号 + device_id = static_cast(data[2]); + + // 解析控制信息 + quint8 byte3 = static_cast(data[3]); + control_info.message_type = byte3 & 0x0F; + control_info.work_status = (byte3 >> 4) & 0x0F; + + // 解析保留字节 + reserved_bytes.resize(8); + for (qint32 i = 0; i < 8; ++i) { + if (4 + i < data.size() - 1) { // 排除校验和字节 + reserved_bytes[i] = data[4 + i]; + } else { + reserved_bytes[i] = 0; + } + } + + return true; +} + +bool RadarUplinkData::validateChecksum(const QByteArray& data) const { + if (data.size() < RADAR_UPLINK_FRAME_LENGTH) { + return false; + } + + quint8 checksum = 0; + // 从第3个字节开始计算(跳过帧头) + for (qint32 i = 2; i < RADAR_UPLINK_FRAME_LENGTH - 1; ++i) { + checksum += static_cast(data.at(i)); + } + + return checksum == static_cast(data.at(RADAR_UPLINK_FRAME_LENGTH - 1)); +} + +void RadarUplinkData::setStartCommand() { + control_info.message_type = 0x04; // 04H报文类型 + control_info.work_status = 0x01; // 工作状态:开始 +} + +void RadarUplinkData::setStopCommand() { + control_info.message_type = 0x04; // 04H报文类型 + control_info.work_status = 0x00; // 工作状态:停止 +} + +bool RadarUplinkData::isStartCommand() const { + return (control_info.message_type == 0x04) && (control_info.work_status == 0x01); +} + +bool RadarUplinkData::isStopCommand() const { + return (control_info.message_type == 0x04) && (control_info.work_status == 0x00); +} + +bool RadarUplinkData::isCommandValid() const { + // 检查报文类型是否为04H + if (control_info.message_type != 0x04) { + return false; + } + + // 检查工作状态是否为有效值(0或1) + if (control_info.work_status > 1) { + return false; + } + + return true; +} + +// RadarWarningDevice 实现 + +RadarWarningDevice::RadarWarningDevice(QObject* parent) + : QObject(parent), device_id_(1) { + // 默认设备ID为1 + threat_threshold_ = ThreatLevel::LEVEL_2; + heartbeat_missed_count_ = 0; + + // Register meta types for Qt signal/slot system + qRegisterMetaType(); + qRegisterMetaType(); +} + +RadarWarningDevice::RadarWarningDevice(quint8 device_id, QObject* parent) + : QObject(parent), device_id_(device_id) { + threat_threshold_ = ThreatLevel::LEVEL_2; + heartbeat_missed_count_ = 0; + + // Register meta types for Qt signal/slot system + qRegisterMetaType(); + qRegisterMetaType(); +} + +QByteArray RadarWarningDevice::createControlCommand(const RadarUplinkData& uplink_data) const { + QByteArray command = uplink_data.serialize(); + + // 设置设备ID + setDeviceIdInData(command, device_id_); + + return command; +} + +bool RadarWarningDevice::parseStatusData(const QByteArray& data) { + RadarDownlinkData downlink_data; + if (!downlink_data.deserialize(data)) { + emit errorOccurred(QStringLiteral("无法解析雷达告警设备状态数据")); + return false; + } + + // 存储最后状态 + ThreatLevel old_threat_level = last_status_.threat_level; + last_status_ = downlink_data; + + emit statusUpdated(downlink_data); + + // 检查威胁等级变化 + if (old_threat_level != downlink_data.threat_level) { + emit threatLevelChanged(old_threat_level, downlink_data.threat_level); + } + + // 检查是否需要触发机动 + if (shouldTriggerManeuver(downlink_data.threat_level, threat_threshold_)) { + emit maneuverSuggested(downlink_data.threat_level, threat_threshold_); + } + + // 如果是心跳报文,重置心跳计数器 + if (downlink_data.message_type == RadarMessageType::HEARTBEAT) { + heartbeat_missed_count_ = 0; + } else { + // 对于非心跳报文,增加心跳丢失计数 + heartbeat_missed_count_++; + if (heartbeat_missed_count_ > 3) { // 连续丢失3次心跳 + emit heartbeatLost(); + } + } + + return true; +} + +bool RadarWarningDevice::parseStatusData(const QByteArray& data, RadarDownlinkData& downlink_data) const { + return downlink_data.deserialize(data); +} + +bool RadarWarningDevice::validateFrameHeader(const QByteArray& data) { + if (data.size() < 2) { + return false; + } + // 注意字节序:示例中显示为90 EB + return (static_cast(data[0]) == RADAR_HEADER2) && (static_cast(data[1]) == RADAR_HEADER1); +} + +bool RadarWarningDevice::validateMessageType(const QByteArray& data, RadarMessageType expected_type) { + if (data.size() < 4) { + return false; + } + return static_cast(static_cast(data[3])) == expected_type; +} + +void RadarWarningDevice::sendControlCommand(const RadarUplinkData& uplink_data) { + QByteArray command = createControlCommand(uplink_data); + emit commandSent(command); + // 实际发送逻辑应由上层实现,这里只发出信号 +} + +void RadarWarningDevice::sendRawCommand(const QByteArray& command_data) { + if (validateFrameHeader(command_data)) { + emit commandSent(command_data); + } else { + emit errorOccurred(QStringLiteral("无效的雷达告警设备指令格式")); + } +} + +QByteArray RadarWarningDevice::createHeartbeatMessage(quint8 device_id, ThreatLevel threat_level) { + RadarDownlinkData heartbeat; + heartbeat.setDeviceId(device_id); + heartbeat.message_type = RadarMessageType::HEARTBEAT; + heartbeat.setThreatLevel(threat_level); + + // 设置保留字节为示例值(可选) + QByteArray reserved1; + reserved1.resize(11); + reserved1[0] = 0x00; reserved1[1] = 0x31; reserved1[2] = 0x03; reserved1[3] = 0x43; + reserved1[4] = 0xA3; reserved1[5] = 0x65; reserved1[6] = 0x69; reserved1[7] = 0xB0; + reserved1[8] = 0x00; reserved1[9] = 0x00; reserved1[10] = 0x00; + + QByteArray reserved2; + reserved2.fill(0, 13); + + heartbeat.reserved_bytes1 = reserved1; + heartbeat.reserved_bytes2 = reserved2; + + return heartbeat.serialize(); +} + +QByteArray RadarWarningDevice::createStartCommand(quint8 device_id) { + RadarUplinkData start_cmd; + start_cmd.device_id = device_id; + start_cmd.setStartCommand(); + + // 设置保留字节为示例值(协议示例:00 00 00 00 00 00 00 02) + QByteArray reserved; + reserved.fill(0, 8); + reserved[7] = 0x02; + start_cmd.reserved_bytes = reserved; + + return start_cmd.serialize(); +} + +QByteArray RadarWarningDevice::createStopCommand(quint8 device_id) { + RadarUplinkData stop_cmd; + stop_cmd.device_id = device_id; + stop_cmd.setStopCommand(); + + // 设置保留字节为示例值(协议示例:00 00 00 00 00 00 00 01) + QByteArray reserved; + reserved.fill(0, 8); + reserved[7] = 0x01; + stop_cmd.reserved_bytes = reserved; + + return stop_cmd.serialize(); +} + +quint8 RadarWarningDevice::calculateChecksum(const QByteArray& data) { + if (data.size() < 3) { + return 0; + } + + quint8 checksum = 0; + // 从第3个字节开始计算(跳过帧头) + for (qint32 i = 2; i < data.size() - 1; ++i) { + checksum += static_cast(data.at(i)); + } + return checksum; +} + +bool RadarWarningDevice::shouldTriggerManeuver(ThreatLevel current_threat, ThreatLevel threshold) { + return static_cast(current_threat) >= static_cast(threshold); +} + +void RadarWarningDevice::setDeviceIdInData(QByteArray& data, quint8 device_id) const { + if (data.size() >= 3) { + data[2] = static_cast(device_id); + } +} + +// 槽函数实现 + +void RadarWarningDevice::setThreatThreshold(payload_protocol::ThreatLevel threshold) { + threat_threshold_ = threshold; + + qDebug() << QString("威胁阈值已设置: %1").arg(static_cast(threshold)); + + // 如果已有状态数据,重新检查机动建议 + if (last_status_.device_info.device_id != 0) { + if (shouldTriggerManeuver(last_status_.threat_level, threat_threshold_)) { + emit maneuverSuggested(last_status_.threat_level, threat_threshold_); + } + } +} + +void RadarWarningDevice::startOperation() { + RadarUplinkData start_cmd; + start_cmd.setStartCommand(); + sendControlCommand(start_cmd); + qDebug() << "开始工作指令已发送"; +} + +void RadarWarningDevice::stopOperation() { + RadarUplinkData stop_cmd; + stop_cmd.setStopCommand(); + sendControlCommand(stop_cmd); + qDebug() << "停止工作指令已发送"; +} + +void RadarWarningDevice::requestHeartbeat() { + // 创建一个简单的心跳请求指令 + RadarUplinkData heartbeat_request; + heartbeat_request.device_id = device_id_; + heartbeat_request.control_info.message_type = 0x04; + heartbeat_request.control_info.work_status = 0x00; // 保持当前状态 + + sendControlCommand(heartbeat_request); + qDebug() << "心跳请求已发送"; +} + +void RadarWarningDevice::resetDevice() { + // 重置设备状态 + threat_threshold_ = ThreatLevel::LEVEL_2; + heartbeat_missed_count_ = 0; + + qDebug() << "设备已重置,威胁阈值恢复为默认值"; + emit errorOccurred(QStringLiteral("设备已重置")); +} + +void RadarWarningDevice::handleStatusUpdate(const payload_protocol::RadarDownlinkData& status) { + // 存储最后状态 + ThreatLevel old_threat_level = last_status_.threat_level; + last_status_ = status; + + // 检查威胁等级变化 + if (old_threat_level != status.threat_level) { + emit threatLevelChanged(old_threat_level, status.threat_level); + } + + // 检查是否需要触发机动 + if (shouldTriggerManeuver(status.threat_level, threat_threshold_)) { + emit maneuverSuggested(status.threat_level, threat_threshold_); + } + + // 发出状态更新信号 + emit statusUpdated(status); +} + +} // namespace payload_protocol \ No newline at end of file diff --git a/App/ToolsUI/Payload/dev/radar_warning_device.h b/App/ToolsUI/Payload/dev/radar_warning_device.h new file mode 100644 index 0000000..d42c1dc --- /dev/null +++ b/App/ToolsUI/Payload/dev/radar_warning_device.h @@ -0,0 +1,257 @@ +#ifndef RADAR_WARNING_DEVICE_H +#define RADAR_WARNING_DEVICE_H + +#include +#include +#include +#include +#include +#include + +namespace payload_protocol { + +// 雷达告警设备识别码(协议中未明确给出,假设值) +constexpr quint8 RADAR_WARNING_DEVICE_ID = 0x03; + +// 雷达告警帧头(注意字节序:示例中显示为0xEB90,但报文示例为90 EB,可能是小端序) +constexpr quint8 RADAR_HEADER1 = 0xEB; +constexpr quint8 RADAR_HEADER2 = 0x90; + +// 雷达告警帧长度 +constexpr qint32 RADAR_UPLINK_FRAME_LENGTH = 13; // 上行帧长度 +constexpr qint32 RADAR_DOWNLINK_FRAME_LENGTH = 30; // 下行帧长度 + +// 报文类型枚举 +enum class RadarMessageType : quint8 { + HEARTBEAT = 0x00, // 00H: 心跳报文 + START_STOP = 0x04 // 04H: 开始停止报文 +}; + +// 工作状态枚举 +enum class RadarWorkStatus : quint8 { + STOP = 0, // 停止 + START = 1 // 开始 +}; + +// 威胁等级枚举(0-5) +enum class ThreatLevel : quint8 { + LEVEL_0 = 0, // 无威胁 + LEVEL_1 = 1, + LEVEL_2 = 2, + LEVEL_3 = 3, + LEVEL_4 = 4, + LEVEL_5 = 5 // 最高威胁 +}; + +// 雷达告警下行数据结构(设备 -> 飞控) +struct RadarDownlinkData { + // 字节0-1: 帧头(在序列化时添加) + + // 字节2: 设备号(低7位) + 保留位(高1位) + struct { + quint8 device_id : 7; // bit0~6: 设备号(0-127) + quint8 reserved_bit : 1; // bit7: 保留位 + } device_info; + + // 字节3: 报文类型 + RadarMessageType message_type; + + // 字节4-14: 保留(11字节) + QByteArray reserved_bytes1; + + // 字节15: 威胁等级(0-5) + ThreatLevel threat_level; + + // 字节16-28: 保留(13字节) + QByteArray reserved_bytes2; + + // 字节29: 校验和(在序列化时计算) + + RadarDownlinkData(); + + // 序列化为字节流 + QByteArray serialize() const; + + // 从字节流反序列化 + bool deserialize(const QByteArray& data); + + // 验证校验和 + bool validateChecksum(const QByteArray& data) const; + + // 检查是否触发告警(用于飞控判断) + bool shouldTriggerAlarm(ThreatLevel set_threat_level) const; + + // 获取威胁等级数值 + quint8 getThreatLevelValue() const; + + // 设置威胁等级 + void setThreatLevel(ThreatLevel level); + + // 设置设备号 + void setDeviceId(quint8 id); + + // 获取设备号 + quint8 getDeviceId() const; +}; + +// 雷达告警上行数据结构(飞控 -> 设备) +struct RadarUplinkData { + // 字节0-1: 帧头(在序列化时添加) + + // 字节2: 设备号(0-255) + quint8 device_id; + + // 字节3: 报文类型(低4位) + 工作状态(高4位) + struct { + quint8 message_type : 4; // bit0~3: 报文类型(04H) + quint8 work_status : 4; // bit4~7: 工作状态(1:开始, 0:停止) + } control_info; + + // 字节4-11: 保留(8字节) + QByteArray reserved_bytes; + + // 字节12: 校验和(在序列化时计算) + + RadarUplinkData(); + + // 序列化为字节流 + QByteArray serialize() const; + + // 从字节流反序列化 + bool deserialize(const QByteArray& data); + + // 验证校验和 + bool validateChecksum(const QByteArray& data) const; + + // 设置开始命令 + void setStartCommand(); + + // 设置停止命令 + void setStopCommand(); + + // 检查是否为开始命令 + bool isStartCommand() const; + + // 检查是否为停止命令 + bool isStopCommand() const; + + // 检查命令是否有效 + bool isCommandValid() const; +}; + +// 雷达告警设备类 - 支持信号槽机制 +class RadarWarningDevice : public QObject { + Q_OBJECT + +public: + explicit RadarWarningDevice(QObject* parent = nullptr); + explicit RadarWarningDevice(quint8 device_id, QObject* parent = nullptr); + + // 获取设备ID + quint8 getDeviceId() const { return device_id_; } + + // 设置设备ID + void setDeviceId(quint8 id) { device_id_ = id; } + + // 创建控制指令(上行数据) + QByteArray createControlCommand(const RadarUplinkData& uplink_data) const; + + // 解析状态数据(下行数据) - 槽函数 + Q_SLOT bool parseStatusData(const QByteArray& data); + + // 手动解析状态数据(兼容原有接口) + bool parseStatusData(const QByteArray& data, RadarDownlinkData& downlink_data) const; + + // 发送控制指令 - 槽函数 + Q_SLOT void sendControlCommand(const RadarUplinkData& uplink_data); + + // 发送原始指令数据 - 槽函数 + Q_SLOT void sendRawCommand(const QByteArray& command_data); + + // 验证帧头 + static bool validateFrameHeader(const QByteArray& data); + + // 验证报文类型 + static bool validateMessageType(const QByteArray& data, RadarMessageType expected_type); + + // 创建心跳报文(下行模拟) + static QByteArray createHeartbeatMessage(quint8 device_id, ThreatLevel threat_level); + + // 创建开始命令 + static QByteArray createStartCommand(quint8 device_id); + + // 创建停止命令 + static QByteArray createStopCommand(quint8 device_id); + + // 计算校验和(用于雷达告警协议,从第3个字节开始计算) + static quint8 calculateChecksum(const QByteArray& data); + + // 检查是否应该触发S型机动(根据威胁等级判断) + static bool shouldTriggerManeuver(ThreatLevel current_threat, ThreatLevel threshold); + +signals: + // 状态更新信号 + void statusUpdated(const payload_protocol::RadarDownlinkData& status); + + // 威胁等级变化信号 + void threatLevelChanged(payload_protocol::ThreatLevel old_level, + payload_protocol::ThreatLevel new_level); + + // 机动建议信号 + void maneuverSuggested(payload_protocol::ThreatLevel current_threat, + payload_protocol::ThreatLevel threshold); + + // 设备连接状态变化信号 + void connectionStatusChanged(bool connected); + + // 心跳丢失信号 + void heartbeatLost(); + + // 指令发送信号 + void commandSent(const QByteArray& command_data); + + // 错误信号 + void errorOccurred(const QString& error_message); + +public slots: + // 设置威胁阈值 + Q_SLOT void setThreatThreshold(payload_protocol::ThreatLevel threshold); + + // 开始工作 + Q_SLOT void startOperation(); + + // 停止工作 + Q_SLOT void stopOperation(); + + // 请求心跳 + Q_SLOT void requestHeartbeat(); + + // 重置设备 + Q_SLOT void resetDevice(); + +private slots: + // 处理内部状态更新 + void handleStatusUpdate(const payload_protocol::RadarDownlinkData& status); + +private: + quint8 device_id_; // 设备ID(0-255) + + // 威胁阈值 + ThreatLevel threat_threshold_ = ThreatLevel::LEVEL_2; + + // 最后接收到的状态 + RadarDownlinkData last_status_; + + // 心跳计数器 + qint32 heartbeat_missed_count_ = 0; + + // 在数据中设置设备ID + void setDeviceIdInData(QByteArray& data, quint8 device_id) const; +}; +} // namespace payload_protocol + +Q_DECLARE_METATYPE(payload_protocol::ThreatLevel) +Q_DECLARE_METATYPE(payload_protocol::RadarDownlinkData) +Q_DECLARE_METATYPE(payload_protocol::RadarUplinkData) + +#endif // RADAR_WARNING_DEVICE_H \ No newline at end of file diff --git a/App/ToolsUI/Payload/dev/uv_warning_device.cpp b/App/ToolsUI/Payload/dev/uv_warning_device.cpp new file mode 100644 index 0000000..0bfb603 --- /dev/null +++ b/App/ToolsUI/Payload/dev/uv_warning_device.cpp @@ -0,0 +1,539 @@ +#include "uv_warning_device.h" +#include +#include +#include + +namespace payload_protocol { + +// UvDownlinkData 实现 + +UvDownlinkData::UvDownlinkData() + : integration_time(0), average_brightness(0), + intensity_threshold(0), range_threshold(0) { + warning_flags.has_target = 0; + warning_flags.target_position = 0; + warning_flags.reserved = 0; + + device_status1.exposure_mode = 0; + device_status1.contrast_mode = 0; + device_status1.gamma_mode = 0; + device_status1.dynamic_range_mode = 0; + device_status1.tracker_serial_status = 0; + device_status1.camera_serial_status = 0; + device_status1.flight_controller_status = 0; + device_status1.image_output_status = 0; + + reserved_bytes.fill(0, 17); +} + +QByteArray UvDownlinkData::serialize() const { + QByteArray data(UV_DOWNLINK_FRAME_LENGTH, 0); + + // 设置帧头 + data[0] = static_cast(UV_HEADER1); + data[1] = static_cast(UV_HEADER2); + + // 设备识别码 + data[2] = static_cast(device_id); + + // 告警标志字节 + data[3] = static_cast((warning_flags.has_target & 0x01) | + ((warning_flags.target_position & 0x03) << 1) | + ((warning_flags.reserved & 0x1F) << 3)); + + // 积分时间(3字节,小端序) + data[4] = static_cast(integration_time & 0xFF); + data[5] = static_cast((integration_time >> 8) & 0xFF); + data[6] = static_cast((integration_time >> 16) & 0xFF); + + // 图像平均亮度(2字节,小端序) + data[7] = static_cast(average_brightness & 0xFF); + data[8] = static_cast((average_brightness >> 8) & 0xFF); + + // 设备状态1 + data[9] = static_cast((device_status1.exposure_mode & 0x01) | + ((device_status1.contrast_mode & 0x01) << 1) | + ((device_status1.gamma_mode & 0x01) << 2) | + ((device_status1.dynamic_range_mode & 0x01) << 3) | + ((device_status1.tracker_serial_status & 0x01) << 4) | + ((device_status1.camera_serial_status & 0x01) << 5) | + ((device_status1.flight_controller_status & 0x01) << 6) | + ((device_status1.image_output_status & 0x01) << 7)); + + // 设备状态2和3 + data[10] = static_cast(intensity_threshold); + data[11] = static_cast(range_threshold); + + // 保留字节 + for (qint32 i = 0; i < reserved_bytes.size() && i < 17; ++i) { + data[12 + i] = reserved_bytes.at(i); + } + + // 计算校验和(前29个字节累加和,取低8位) + quint8 checksum = 0; + for (qint32 i = 0; i < UV_DOWNLINK_FRAME_LENGTH - 1; ++i) { + checksum += static_cast(data.at(i)); + } + data[UV_DOWNLINK_FRAME_LENGTH - 1] = static_cast(checksum); + + return data; +} + +bool UvDownlinkData::deserialize(const QByteArray& data) { + if (data.size() < UV_DOWNLINK_FRAME_LENGTH) { + return false; + } + + // 验证帧头 + if (static_cast(data[0]) != UV_HEADER1 || static_cast(data[1]) != UV_HEADER2) { + return false; + } + + // 验证设备识别码 + if (static_cast(data[2]) != UV_WARNING_DEVICE_ID) { + return false; + } + + // 验证校验和 + if (!validateChecksum(data)) { + return false; + } + + // 解析告警标志字节 + quint8 byte3 = static_cast(data[3]); + warning_flags.has_target = byte3 & 0x01; + warning_flags.target_position = (byte3 >> 1) & 0x03; + warning_flags.reserved = (byte3 >> 3) & 0x1F; + + // 解析积分时间(3字节,小端序) + integration_time = static_cast(static_cast(data[4])) | + (static_cast(static_cast(data[5])) << 8) | + (static_cast(static_cast(data[6])) << 16); + + // 解析图像平均亮度(2字节,小端序) + average_brightness = static_cast(static_cast(data[7])) | + (static_cast(static_cast(data[8])) << 8); + + // 解析设备状态1 + quint8 byte9 = static_cast(data[9]); + device_status1.exposure_mode = byte9 & 0x01; + device_status1.contrast_mode = (byte9 >> 1) & 0x01; + device_status1.gamma_mode = (byte9 >> 2) & 0x01; + device_status1.dynamic_range_mode = (byte9 >> 3) & 0x01; + device_status1.tracker_serial_status = (byte9 >> 4) & 0x01; + device_status1.camera_serial_status = (byte9 >> 5) & 0x01; + device_status1.flight_controller_status = (byte9 >> 6) & 0x01; + device_status1.image_output_status = (byte9 >> 7) & 0x01; + + // 解析设备状态2和3 + intensity_threshold = static_cast(data[10]); + range_threshold = static_cast(data[11]); + + // 解析保留字节 + reserved_bytes.resize(17); + for (qint32 i = 0; i < 17; ++i) { + if (12 + i < data.size() - 1) { // 排除校验和字节 + reserved_bytes[i] = data[12 + i]; + } else { + reserved_bytes[i] = 0; + } + } + + return true; +} + +bool UvDownlinkData::validateChecksum(const QByteArray& data) const { + if (data.size() < UV_DOWNLINK_FRAME_LENGTH) { + return false; + } + + quint8 checksum = 0; + for (qint32 i = 0; i < UV_DOWNLINK_FRAME_LENGTH - 1; ++i) { + checksum += static_cast(data.at(i)); + } + + return checksum == static_cast(data.at(UV_DOWNLINK_FRAME_LENGTH - 1)); +} + +bool UvDownlinkData::shouldTriggerAlarm(quint8 set_intensity_threshold, + quint8 set_range_threshold) const { + return (intensity_threshold >= set_intensity_threshold) && + (range_threshold >= set_range_threshold); +} + +TargetPosition UvDownlinkData::getTargetPosition() const { + if (!warning_flags.has_target) { + return TargetPosition::NO_TARGET; + } + + switch (warning_flags.target_position) { + case 0b01: return TargetPosition::LEFT_FIELD; + case 0b10: return TargetPosition::RIGHT_FIELD; + case 0b00: return TargetPosition::CENTER_FIELD; + default: return TargetPosition::NO_TARGET; + } +} + +void UvDownlinkData::setTargetPosition(TargetPosition position, bool has_target) { + warning_flags.has_target = has_target ? 1 : 0; + + if (has_target) { + if (position == TargetPosition::LEFT_FIELD) { + warning_flags.target_position = 0b01; + } else if (position == TargetPosition::RIGHT_FIELD) { + warning_flags.target_position = 0b10; + } else { + // For CENTER_FIELD or any other value (including NO_TARGET when has_target is true) + warning_flags.target_position = 0b00; + } + } else { + warning_flags.target_position = 0b00; + } +} + +// UvUplinkData 实现 + +UvUplinkData::UvUplinkData() + : intensity_level(0), range_level(0) { + command_byte.exposure_mode = 0; + command_byte.contrast_mode = 0; + command_byte.gamma_mode = 0; + command_byte.dynamic_range_mode = 0; + command_byte.exposure_increase = 0; + command_byte.exposure_decrease = 0; + command_byte.set_intensity_threshold = 0; + command_byte.set_range_threshold = 0; + + reserved_bytes.fill(0, 6); +} + +QByteArray UvUplinkData::serialize() const { + QByteArray data(UV_UPLINK_FRAME_LENGTH, 0); + + // 设置帧头 + data[0] = static_cast(UV_HEADER1); + data[1] = static_cast(UV_HEADER2); + + // 设备识别码 + data[2] = static_cast(device_id); + + // 告警设备命令字节 + data[3] = static_cast((command_byte.exposure_mode & 0x01) | + ((command_byte.contrast_mode & 0x01) << 1) | + ((command_byte.gamma_mode & 0x01) << 2) | + ((command_byte.dynamic_range_mode & 0x01) << 3) | + ((command_byte.exposure_increase & 0x01) << 4) | + ((command_byte.exposure_decrease & 0x01) << 5) | + ((command_byte.set_intensity_threshold & 0x01) << 6) | + ((command_byte.set_range_threshold & 0x01) << 7)); + + // 强度档位参数 + data[4] = static_cast(intensity_level); + + // 范围档位参数 + data[5] = static_cast(range_level); + + // 保留字节 + for (qint32 i = 0; i < reserved_bytes.size() && i < 6; ++i) { + data[6 + i] = reserved_bytes.at(i); + } + + // 计算校验和(前12个字节累加和,取低8位) + quint8 checksum = 0; + for (qint32 i = 0; i < UV_UPLINK_FRAME_LENGTH - 1; ++i) { + checksum += static_cast(data.at(i)); + } + data[UV_UPLINK_FRAME_LENGTH - 1] = static_cast(checksum); + + return data; +} + +bool UvUplinkData::deserialize(const QByteArray& data) { + if (data.size() < UV_UPLINK_FRAME_LENGTH) { + return false; + } + + // 验证帧头 + if (static_cast(data[0]) != UV_HEADER1 || static_cast(data[1]) != UV_HEADER2) { + return false; + } + + // 验证设备识别码 + if (static_cast(data[2]) != UV_WARNING_DEVICE_ID) { + return false; + } + + // 验证校验和 + if (!validateChecksum(data)) { + return false; + } + + // 解析命令字节 + quint8 byte3 = static_cast(data[3]); + command_byte.exposure_mode = byte3 & 0x01; + command_byte.contrast_mode = (byte3 >> 1) & 0x01; + command_byte.gamma_mode = (byte3 >> 2) & 0x01; + command_byte.dynamic_range_mode = (byte3 >> 3) & 0x01; + command_byte.exposure_increase = (byte3 >> 4) & 0x01; + command_byte.exposure_decrease = (byte3 >> 5) & 0x01; + command_byte.set_intensity_threshold = (byte3 >> 6) & 0x01; + command_byte.set_range_threshold = (byte3 >> 7) & 0x01; + + // 解析强度档位参数 + intensity_level = static_cast(data[4]); + + // 解析范围档位参数 + range_level = static_cast(data[5]); + + // 解析保留字节 + reserved_bytes.resize(6); + for (qint32 i = 0; i < 6; ++i) { + if (6 + i < data.size() - 1) { // 排除校验和字节 + reserved_bytes[i] = data[6 + i]; + } else { + reserved_bytes[i] = 0; + } + } + + return true; +} + +bool UvUplinkData::validateChecksum(const QByteArray& data) const { + if (data.size() < UV_UPLINK_FRAME_LENGTH) { + return false; + } + + quint8 checksum = 0; + for (qint32 i = 0; i < UV_UPLINK_FRAME_LENGTH - 1; ++i) { + checksum += static_cast(data.at(i)); + } + + return checksum == static_cast(data.at(UV_UPLINK_FRAME_LENGTH - 1)); +} + +void UvUplinkData::setExposureAdjustment(bool increase) { + if (increase) { + command_byte.exposure_increase = 1; + command_byte.exposure_decrease = 0; + command_byte.exposure_mode = 1; // 手动曝光模式 + } else { + command_byte.exposure_increase = 0; + command_byte.exposure_decrease = 1; + command_byte.exposure_mode = 1; // 手动曝光模式 + } +} + +void UvUplinkData::setThresholds(quint8 intensity, quint8 range, + bool set_intensity, bool set_range) { + intensity_level = intensity; + range_level = range; + command_byte.set_intensity_threshold = set_intensity ? 1 : 0; + command_byte.set_range_threshold = set_range ? 1 : 0; +} + +bool UvUplinkData::isCommandValid() const { + // 检查曝光调整命令:不能同时增加和减少 + if (command_byte.exposure_increase && command_byte.exposure_decrease) { + return false; + } + + // 检查阈值设置命令:如果设置了阈值,对应的档位参数应该有效 + if (command_byte.set_intensity_threshold && intensity_level == 0) { + return false; + } + + if (command_byte.set_range_threshold && range_level == 0) { + return false; + } + + return true; +} + +// UvWarningDevice 实现 + +UvWarningDevice::UvWarningDevice(QObject* parent) + : QObject(parent), device_number_(0x01) { + // 默认设备编号为1 + intensity_threshold_ = 50; + range_threshold_ = 30; + + // Register meta types for Qt signal/slot system + qRegisterMetaType(); + qRegisterMetaType(); +} + +UvWarningDevice::UvWarningDevice(quint8 device_number, QObject* parent) + : QObject(parent), device_number_(device_number) { + intensity_threshold_ = 50; + range_threshold_ = 30; + + // Register meta types for Qt signal/slot system + qRegisterMetaType(); + qRegisterMetaType(); +} + +QByteArray UvWarningDevice::createControlCommand(const UvUplinkData& uplink_data) const { + QByteArray command = uplink_data.serialize(); + + // 在控制数据中插入设备编号 + // 根据协议,控制数据(第4-12字节)中应该包含设备编号信息 + // 这里我们在保留字节的第一个位置插入设备编号 + if (command.size() >= 7) { // 至少7字节才能访问到第6字节(保留字节的开始) + command[6] = static_cast(device_number_); // 在第一个保留字节位置插入设备编号 + } + + return command; +} + +bool UvWarningDevice::parseStatusData(const QByteArray& data) { + UvDownlinkData downlink_data; + if (!downlink_data.deserialize(data)) { + emit errorOccurred(QStringLiteral("无法解析紫外告警设备状态数据")); + return false; + } + + last_status_ = downlink_data; + emit statusUpdated(downlink_data); + + // 检查目标检测 + if (downlink_data.warning_flags.has_target) { + emit targetDetected(downlink_data.getTargetPosition()); + } + + // 检查告警触发 + if (downlink_data.shouldTriggerAlarm(intensity_threshold_, range_threshold_)) { + emit alarmTriggered(downlink_data, intensity_threshold_, range_threshold_); + } + + return true; +} + +bool UvWarningDevice::parseStatusData(const QByteArray& data, UvDownlinkData& downlink_data) const { + return downlink_data.deserialize(data); +} + +bool UvWarningDevice::validateFrameHeader(const QByteArray& data) { + if (data.size() < 2) { + return false; + } + return (static_cast(data[0]) == UV_HEADER1) && (static_cast(data[1]) == UV_HEADER2); +} + +bool UvWarningDevice::validateDeviceId(const QByteArray& data) { + if (data.size() < 3) { + return false; + } + return static_cast(data[2]) == UV_WARNING_DEVICE_ID; +} + +void UvWarningDevice::sendControlCommand(const UvUplinkData& uplink_data) { + QByteArray command = createControlCommand(uplink_data); + emit commandSent(command); + // 实际发送逻辑应由上层实现,这里只发出信号 +} + +void UvWarningDevice::sendRawCommand(const QByteArray& command_data) { + if (validateFrameHeader(command_data) && validateDeviceId(command_data)) { + emit commandSent(command_data); + } else { + emit errorOccurred(QStringLiteral("无效的紫外告警设备指令格式")); + } +} + +QByteArray UvWarningDevice::createBroadcastCommand(const UvUplinkData& uplink_data) { + QByteArray command = uplink_data.serialize(); + + // 广播命令:设备编号为0x00 + if (command.size() >= 7) { + command[6] = static_cast(0x00); // 设备编号0x00表示全控 + } + + return command; +} + +quint8 UvWarningDevice::calculateChecksum(const QByteArray& data, qint32 length) { + if (length == 0) { + length = data.size(); + } + + quint8 checksum = 0; + for (qint32 i = 0; i < length && i < data.size(); ++i) { + checksum += static_cast(data.at(i)); + } + return checksum; +} + +void UvWarningDevice::insertDeviceNumber(QByteArray& data) const { + if (data.size() >= 7) { + data[6] = static_cast(device_number_); + } +} + +// 槽函数实现 + +void UvWarningDevice::setAlarmThresholds(quint8 intensity_threshold, quint8 range_threshold) { + intensity_threshold_ = intensity_threshold; + range_threshold_ = range_threshold; + + qDebug() << QString("告警阈值已设置: 强度=%1, 范围=%2") + .arg(intensity_threshold_) + .arg(range_threshold_); + + // 如果已有状态数据,重新检查告警 + if (last_status_.device_id == UV_WARNING_DEVICE_ID) { + if (last_status_.shouldTriggerAlarm(intensity_threshold_, range_threshold_)) { + emit alarmTriggered(last_status_, intensity_threshold_, range_threshold_); + } + } +} + +void UvWarningDevice::resetDevice() { + // 重置设备状态 + intensity_threshold_ = 50; + range_threshold_ = 30; + + qDebug() << "设备已重置,告警阈值恢复为默认值"; + emit errorOccurred(QStringLiteral("设备已重置")); +} + +void UvWarningDevice::requestStatusUpdate() { + // 创建一个状态查询指令 + UvUplinkData uplink_data; + // 清空所有命令位,表示状态查询 + uplink_data.command_byte.exposure_mode = 0; + uplink_data.command_byte.contrast_mode = 0; + uplink_data.command_byte.gamma_mode = 0; + uplink_data.command_byte.dynamic_range_mode = 0; + uplink_data.command_byte.exposure_increase = 0; + uplink_data.command_byte.exposure_decrease = 0; + uplink_data.command_byte.set_intensity_threshold = 0; + uplink_data.command_byte.set_range_threshold = 0; + + sendControlCommand(uplink_data); + qDebug() << "状态更新请求已发送"; +} + +void UvWarningDevice::handleStatusUpdate(const payload_protocol::UvDownlinkData& status) { + // 存储最后状态 + last_status_ = status; + + // 检查连接状态变化 + bool is_connected = status.device_status1.camera_serial_status && + status.device_status1.flight_controller_status && + status.device_status1.image_output_status; + + // 发出状态更新信号 + emit statusUpdated(status); + + // 检查目标检测 + if (status.warning_flags.has_target) { + emit targetDetected(status.getTargetPosition()); + } + + // 检查告警触发 + if (status.shouldTriggerAlarm(intensity_threshold_, range_threshold_)) { + emit alarmTriggered(status, intensity_threshold_, range_threshold_); + } +} + +} // namespace payload_protocol \ No newline at end of file diff --git a/App/ToolsUI/Payload/dev/uv_warning_device.h b/App/ToolsUI/Payload/dev/uv_warning_device.h new file mode 100644 index 0000000..dba5f13 --- /dev/null +++ b/App/ToolsUI/Payload/dev/uv_warning_device.h @@ -0,0 +1,267 @@ +#ifndef UV_WARNING_DEVICE_H +#define UV_WARNING_DEVICE_H + +#include +#include +#include +#include +#include +#include + +namespace payload_protocol { + +// 紫外告警设备识别码 +constexpr quint8 UV_WARNING_DEVICE_ID = 0x02; + +// 紫外告警帧头 +constexpr quint8 UV_HEADER1 = 0xDD; +constexpr quint8 UV_HEADER2 = 0x77; + +// 紫外告警帧长度 +constexpr qint32 UV_UPLINK_FRAME_LENGTH = 13; // 上行帧长度 +constexpr qint32 UV_DOWNLINK_FRAME_LENGTH = 30; // 下行帧长度 + +// 目标位置枚举 +enum class TargetPosition : quint8 { + NO_TARGET = 0b00, // 无目标 + LEFT_FIELD = 0b01, // 左侧视场 + RIGHT_FIELD = 0b10, // 右侧视场 + CENTER_FIELD = 0b00 // 中间视场(bit2~bit1为00时) +}; + +// 曝光模式 +enum class ExposureMode : quint8 { + AUTO = 0, + MANUAL = 1 +}; + +// 对比度模式 +enum class ContrastMode : quint8 { + AUTO = 0, + MANUAL = 1 +}; + +// Gamma模式 +enum class GammaMode : quint8 { + OFF = 0, + ON = 1 +}; + +// 动态范围压缩模式 +enum class DynamicRangeMode : quint8 { + OFF = 0, + ON = 1 +}; + +// 紫外告警下行数据结构(设备 -> 飞控) +struct UvDownlinkData { + // 字节0-1: 帧头(在序列化时添加) + + // 字节2: 设备识别码 + quint8 device_id = UV_WARNING_DEVICE_ID; + + // 字节3: 告警标志字节 + struct { + quint8 has_target : 1; // bit0: 1有目标,0无目标 + quint8 target_position : 2; // bit2~bit1: 目标位置 + quint8 reserved : 5; // bit7~bit3: 保留 + } warning_flags; + + // 字节4-6: 积分时间(3字节) + quint32 integration_time; // 24位 + + // 字节7-8: 图像平均亮度(2字节) + quint16 average_brightness; + + // 字节9: 设备状态1 + struct { + quint8 exposure_mode : 1; // bit0: 1自动曝光,0手动曝光 + quint8 contrast_mode : 1; // bit1: 1自动对比度,0手动对比度 + quint8 gamma_mode : 1; // bit2: 1GAMMA开启,0GAMMA关闭 + quint8 dynamic_range_mode : 1; // bit3: 1动态范围压缩开启,0关闭 + quint8 tracker_serial_status : 1; // bit4: 1跟踪器串口正常,0异常 + quint8 camera_serial_status : 1; // bit5: 1相机串口正常,0异常 + quint8 flight_controller_status : 1; // bit6: 1飞控串口正常,0异常 + quint8 image_output_status : 1; // bit7: 1图像输出正常,0异常 + } device_status1; + + // 字节10: 设备状态2 - 当前检测目标强度阈值 + quint8 intensity_threshold; + + // 字节11: 设备状态3 - 当前检测范围阈值 + quint8 range_threshold; + + // 字节12-28: 保留(17字节) + QByteArray reserved_bytes; + + // 字节29: 校验和(在序列化时计算) + + UvDownlinkData(); + + // 序列化为字节流 + QByteArray serialize() const; + + // 从字节流反序列化 + bool deserialize(const QByteArray& data); + + // 验证校验和 + bool validateChecksum(const QByteArray& data) const; + + // 检查是否触发告警(用于飞控判断) + bool shouldTriggerAlarm(quint8 set_intensity_threshold, quint8 set_range_threshold) const; + + // 获取目标位置 + TargetPosition getTargetPosition() const; + + // 设置目标位置 + void setTargetPosition(TargetPosition position, bool has_target); +}; + +// 紫外告警上行数据结构(飞控 -> 设备) +struct UvUplinkData { + // 字节0-1: 帧头(在序列化时添加) + + // 字节2: 设备识别码 + quint8 device_id = UV_WARNING_DEVICE_ID; + + // 字节3: 告警设备命令字节 + struct { + quint8 exposure_mode : 1; // bit0: 0自动曝光,1手动曝光 + quint8 contrast_mode : 1; // bit1: 0自动对比度,1手动对比度 + quint8 gamma_mode : 1; // bit2: 1GAMMA开启,0GAMMA关闭 + quint8 dynamic_range_mode : 1; // bit3: 1动态范围压缩开启,0关闭 + quint8 exposure_increase : 1; // bit4: 1曝光时间加100微秒 + quint8 exposure_decrease : 1; // bit5: 1曝光时间减100微秒 + quint8 set_intensity_threshold : 1; // bit6: 1设置强度阈值 + quint8 set_range_threshold : 1; // bit7: 1设置范围阈值 + } command_byte; + + // 字节4: 强度档位参数(配合command_byte.bit6) + quint8 intensity_level; + + // 字节5: 范围档位参数(配合command_byte.bit7) + quint8 range_level; + + // 字节6-11: 保留(6字节) + QByteArray reserved_bytes; + + // 字节12: 校验和(在序列化时计算) + + UvUplinkData(); + + // 序列化为字节流 + QByteArray serialize() const; + + // 从字节流反序列化 + bool deserialize(const QByteArray& data); + + // 验证校验和 + bool validateChecksum(const QByteArray& data) const; + + // 设置曝光调整命令 + void setExposureAdjustment(bool increase); + + // 设置阈值命令 + void setThresholds(quint8 intensity, quint8 range, bool set_intensity, bool set_range); + + // 检查命令是否有效 + bool isCommandValid() const; +}; + +// 紫外告警设备类 - 支持信号槽机制 +class UvWarningDevice : public QObject { + Q_OBJECT + +public: + explicit UvWarningDevice(QObject* parent = nullptr); + explicit UvWarningDevice(quint8 device_number, QObject* parent = nullptr); + + // 获取设备编号 + quint8 getDeviceNumber() const { return device_number_; } + + // 设置设备编号 + void setDeviceNumber(quint8 number) { device_number_ = number; } + + // 创建控制指令(上行数据) + QByteArray createControlCommand(const UvUplinkData& uplink_data) const; + + // 解析状态数据(下行数据) - 槽函数 + Q_SLOT bool parseStatusData(const QByteArray& data); + + // 手动解析状态数据(兼容原有接口) + bool parseStatusData(const QByteArray& data, UvDownlinkData& downlink_data) const; + + // 发送控制指令 - 槽函数 + Q_SLOT void sendControlCommand(const UvUplinkData& uplink_data); + + // 发送原始指令数据 - 槽函数 + Q_SLOT void sendRawCommand(const QByteArray& command_data); + + // 验证帧头 + static bool validateFrameHeader(const QByteArray& data); + + // 验证设备识别码 + static bool validateDeviceId(const QByteArray& data); + + // 创建全控指令(设备编号为0x00) + static QByteArray createBroadcastCommand(const UvUplinkData& uplink_data); + + // 计算校验和(用于紫外告警协议) + static quint8 calculateChecksum(const QByteArray& data, qint32 length = 0); + +signals: + // 状态更新信号 + void statusUpdated(const payload_protocol::UvDownlinkData& status); + + // 告警触发信号 + void alarmTriggered(const payload_protocol::UvDownlinkData& status, + quint8 set_intensity_threshold, + quint8 set_range_threshold); + + // 目标检测信号 + void targetDetected(payload_protocol::TargetPosition position); + + // 设备连接状态变化信号 + void connectionStatusChanged(bool connected); + + // 指令发送信号 + void commandSent(const QByteArray& command_data); + + // 错误信号 + void errorOccurred(const QString& error_message); + +public slots: + // 设置告警阈值 + Q_SLOT void setAlarmThresholds(quint8 intensity_threshold, quint8 range_threshold); + + // 重置设备 + Q_SLOT void resetDevice(); + + // 请求状态更新 + Q_SLOT void requestStatusUpdate(); + +private slots: + // 处理内部状态更新 + void handleStatusUpdate(const payload_protocol::UvDownlinkData& status); + +private: + quint8 device_number_; // 设备编号:0x00=全控,0x01=1号设备,0x02=2号设备... + + // 告警阈值 + quint8 intensity_threshold_ = 50; + quint8 range_threshold_ = 30; + + // 最后接收到的状态 + UvDownlinkData last_status_; + + // 在控制数据中插入设备编号 + void insertDeviceNumber(QByteArray& data) const; +}; + +} // namespace payload_protocol + +Q_DECLARE_METATYPE(payload_protocol::TargetPosition) +Q_DECLARE_METATYPE(payload_protocol::UvDownlinkData) +Q_DECLARE_METATYPE(payload_protocol::UvUplinkData) + +#endif // UV_WARNING_DEVICE_H \ No newline at end of file diff --git a/App/ToolsUI/Payload/fail.png b/App/ToolsUI/Payload/fail.png new file mode 100644 index 0000000..da612f0 Binary files /dev/null and b/App/ToolsUI/Payload/fail.png differ diff --git a/App/ToolsUI/Payload/fail_rect.png b/App/ToolsUI/Payload/fail_rect.png new file mode 100644 index 0000000..902b1e7 Binary files /dev/null and b/App/ToolsUI/Payload/fail_rect.png differ diff --git a/App/ToolsUI/Payload/inital.png b/App/ToolsUI/Payload/inital.png new file mode 100644 index 0000000..c340803 Binary files /dev/null and b/App/ToolsUI/Payload/inital.png differ diff --git a/App/ToolsUI/Payload/mybutton.cpp b/App/ToolsUI/Payload/mybutton.cpp new file mode 100644 index 0000000..3ca7f7b --- /dev/null +++ b/App/ToolsUI/Payload/mybutton.cpp @@ -0,0 +1,13 @@ +#include "MyButton.h" +#include + +MyButton::MyButton(QWidget *parent) + : QPushButton(parent) // 必须调用直接基类的构造函数 +{ +} + +void MyButton::mouseDoubleClickEvent(QMouseEvent *event) +{ + emit doubleClicked(); // 发射自定义双击信号 + QPushButton::mouseDoubleClickEvent(event); // 保留原有行为(可选) +} diff --git a/App/ToolsUI/Payload/mybutton.h b/App/ToolsUI/Payload/mybutton.h new file mode 100644 index 0000000..9345f70 --- /dev/null +++ b/App/ToolsUI/Payload/mybutton.h @@ -0,0 +1,19 @@ +#ifndef MYBUTTON_H +#define MYBUTTON_H + +#include + +class MyButton : public QPushButton { + Q_OBJECT +public: + explicit MyButton(QWidget *parent = nullptr); + +signals: + void doubleClicked(); + +protected: + void mouseDoubleClickEvent(QMouseEvent *event) override; +}; + + +#endif // MYBUTTON_H diff --git a/App/ToolsUI/Payload/normal.png b/App/ToolsUI/Payload/normal.png new file mode 100644 index 0000000..851cb3a Binary files /dev/null and b/App/ToolsUI/Payload/normal.png differ diff --git a/App/ToolsUI/Payload/success_rect.png b/App/ToolsUI/Payload/success_rect.png new file mode 100644 index 0000000..f63e87f Binary files /dev/null and b/App/ToolsUI/Payload/success_rect.png differ diff --git a/App/ToolsUI/Payload/warning.png b/App/ToolsUI/Payload/warning.png new file mode 100644 index 0000000..6993ea1 Binary files /dev/null and b/App/ToolsUI/Payload/warning.png differ diff --git a/App/ToolsUI/Payload/warning_rect.png b/App/ToolsUI/Payload/warning_rect.png new file mode 100644 index 0000000..ab07443 Binary files /dev/null and b/App/ToolsUI/Payload/warning_rect.png differ diff --git a/App/ToolsUI/ToolsUI.cpp b/App/ToolsUI/ToolsUI.cpp index ef493f3..ac8915e 100644 --- a/App/ToolsUI/ToolsUI.cpp +++ b/App/ToolsUI/ToolsUI.cpp @@ -30,6 +30,8 @@ ToolsUI::ToolsUI(QWidget *parent) : //ui->pushButton_Diagram->setFixedSize(w,h); ui->pushButton_Senser->setFixedSize(w,h); + ui->pushButton_Payload->setFixedSize(w,h); + index0 = new Tools_Index0(ui->frame); @@ -68,6 +70,23 @@ ToolsUI::ToolsUI(QWidget *parent) : senser->show(); + + payload = new Payload(ui->frame); + + + + payload->show(); + + + + + + + + + + + connect(this,SIGNAL(IndexChanged(int)), this,SLOT(onTabIndexChanged(int))); @@ -99,6 +118,9 @@ ToolsUI::ToolsUI(QWidget *parent) : setPushButtonState(ui->pushButton_Senser, state::unSelected); + setPushButtonState(ui->pushButton_Payload, + state::unSelected); + this->resize(parent->size()); @@ -155,6 +177,8 @@ void ToolsUI::resizeEvent(QResizeEvent *event) senser->setGeometry(0,0,ui->frame->geometry().width(),ui->frame->geometry().height()); + payload->setGeometry(0,0,ui->frame->geometry().width(),ui->frame->geometry().height()); + update(); } @@ -349,6 +373,25 @@ void ToolsUI::onTabIndexChanged(const int &index)//界面选择管理 senser->hide(); } + if(index == 10) + { + if(payload->parent()) + { + payload->show(); + payload->updateUI(); + } + else + { + payload->showNormal(); + payload->activateWindow(); + payload->updateUI(); + } + } + else { + if(payload->parent()) + payload->hide(); + } + } @@ -385,6 +428,9 @@ void ToolsUI::on_pushButton_Inspector_clicked() setPushButtonState(ui->pushButton_Senser, state::unSelected); + setPushButtonState(ui->pushButton_Payload, + state::unSelected); + emit IndexChanged(CurrentIndex); } @@ -421,6 +467,9 @@ void ToolsUI::on_pushButton_Log_clicked() setPushButtonState(ui->pushButton_Senser, state::unSelected); + setPushButtonState(ui->pushButton_Payload, + state::unSelected); + emit IndexChanged(CurrentIndex); } @@ -457,6 +506,9 @@ void ToolsUI::on_pushButton_Replay_clicked() setPushButtonState(ui->pushButton_Senser, state::unSelected); + setPushButtonState(ui->pushButton_Payload, + state::unSelected); + emit IndexChanged(CurrentIndex); } @@ -493,6 +545,9 @@ void ToolsUI::on_pushButton_Console_clicked() setPushButtonState(ui->pushButton_Senser, state::unSelected); + setPushButtonState(ui->pushButton_Payload, + state::unSelected); + emit IndexChanged(CurrentIndex); } @@ -529,6 +584,9 @@ void ToolsUI::on_PowerSystem_clicked() setPushButtonState(ui->pushButton_Senser, state::unSelected); + setPushButtonState(ui->pushButton_Payload, + state::unSelected); + emit IndexChanged(CurrentIndex); } @@ -565,6 +623,9 @@ void ToolsUI::on_ServoSystem_clicked() setPushButtonState(ui->pushButton_Senser, state::unSelected); + setPushButtonState(ui->pushButton_Payload, + state::unSelected); + emit IndexChanged(CurrentIndex); } @@ -602,7 +663,9 @@ void ToolsUI::on_pushButton_ShowExtern_clicked() break; case 9: senser->setFloat(); - + break; + case 10: + payload->setFloat(); break; } } @@ -641,5 +704,48 @@ void ToolsUI::on_pushButton_Senser_clicked() setPushButtonState(ui->pushButton_Senser, state::Selected); + setPushButtonState(ui->pushButton_Payload, + state::unSelected); + + emit IndexChanged(CurrentIndex); +} + +void ToolsUI::on_pushButton_Payload_clicked() +{ + CurrentIndex = 10; + + setPushButtonState(ui->pushButton_Inspector, + state::unSelected); + + setPushButtonState(ui->pushButton_Log, + state::unSelected); + + setPushButtonState(ui->pushButton_Replay, + state::unSelected); + + setPushButtonState(ui->pushButton_Console, + state::unSelected); + + setPushButtonState(ui->PowerSystem, + state::unSelected); + + setPushButtonState(ui->ServoSystem, + state::unSelected); +/* + setPushButtonState(ui->pushButton_CommandBox, + state::unSelected); + */ + +/* + setPushButtonState(ui->pushButton_Diagram, + state::unSelected); + */ + + setPushButtonState(ui->pushButton_Senser, + state::unSelected); + + setPushButtonState(ui->pushButton_Payload, + state::Selected); + emit IndexChanged(CurrentIndex); } diff --git a/App/ToolsUI/ToolsUI.h b/App/ToolsUI/ToolsUI.h index dcd6315..27cb2e1 100644 --- a/App/ToolsUI/ToolsUI.h +++ b/App/ToolsUI/ToolsUI.h @@ -17,6 +17,7 @@ //#include "CommandBox/CommandBox.h" //#include "Diagram/Diagram.h" #include "Senser/Senser.h" +#include "Payload/Payload.h" namespace Ui { class ToolsUI; @@ -55,6 +56,8 @@ public: Senser *senser = nullptr; + Payload *payload = nullptr; + private slots: void setPushButtonState(QWidget *w,state s); void onTabIndexChanged(const int &index); @@ -81,6 +84,8 @@ private slots: void on_pushButton_Senser_clicked(); + void on_pushButton_Payload_clicked(); + signals: void IndexChanged(const int &index); diff --git a/App/ToolsUI/ToolsUI.pri b/App/ToolsUI/ToolsUI.pri index dcbf5fb..04a1b3e 100644 --- a/App/ToolsUI/ToolsUI.pri +++ b/App/ToolsUI/ToolsUI.pri @@ -17,6 +17,7 @@ include (./PowerSystem/PowerSystem.pri) include (./ServoSystem/ServoSystem.pri) include (./Diagram/Diagram.pri) include (./Senser/Senser.pri) +include (./Payload/Payload.pri) INCLUDEPATH += $$PWD/tools_Index0 \ diff --git a/App/ToolsUI/ToolsUI.ui b/App/ToolsUI/ToolsUI.ui index 11d1b56..f4a2766 100644 --- a/App/ToolsUI/ToolsUI.ui +++ b/App/ToolsUI/ToolsUI.ui @@ -23,39 +23,7 @@ - - - - - 0 - 0 - - - - PowerSystem - - - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - Inspector - - - - + Qt::Vertical @@ -68,32 +36,16 @@ - - + + 0 0 - - - 16777215 - 16777215 - - - Log - - - - - - - background-color: rgb(107, 255, 246); - - - 弹出右侧窗口-> + PowerSystem @@ -135,10 +87,13 @@ - - + + + + background-color: rgb(107, 255, 246); + - Senser + 弹出右侧窗口-> @@ -155,6 +110,58 @@ + + + + Senser + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Log + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Inspector + + + + + + + Payload + + + diff --git a/App/ToolsUI/tools_Index0/MAVLinkInspector/MAVLinkInspector.cc b/App/ToolsUI/tools_Index0/MAVLinkInspector/MAVLinkInspector.cc index 61b180d..73be99b 100644 --- a/App/ToolsUI/tools_Index0/MAVLinkInspector/MAVLinkInspector.cc +++ b/App/ToolsUI/tools_Index0/MAVLinkInspector/MAVLinkInspector.cc @@ -485,6 +485,7 @@ void MAVLinkInspector::updateField(mavlink_message_t* msg, const mavlink_message return; } +//#define MAVLINK_VERSION_2 #ifdef MAVLINK_VERSION_2 uint8_t* m = (uint8_t*)&uasMessage->payload64[0]; diff --git a/App/ToolsUI/tools_Index0/MAVLinkInspector/MAVLinkInspector.h b/App/ToolsUI/tools_Index0/MAVLinkInspector/MAVLinkInspector.h index 09bad06..8b557f3 100644 --- a/App/ToolsUI/tools_Index0/MAVLinkInspector/MAVLinkInspector.h +++ b/App/ToolsUI/tools_Index0/MAVLinkInspector/MAVLinkInspector.h @@ -1,4 +1,4 @@ -#ifndef MAVLINKINSPECTOR_H +#ifndef MAVLINKINSPECTOR_H #define MAVLINKINSPECTOR_H #define MAVLINK_USE_MESSAGE_INFO @@ -9,7 +9,7 @@ #include "QDateTime" #include "QWidget" #include "mavlink.h" -#include "mavlink_get_info.h" +//#include "mavlink_get_info.h" #include "Scope.h" diff --git a/App/main.cpp b/App/main.cpp index f0ef396..fe7d049 100644 --- a/App/main.cpp +++ b/App/main.cpp @@ -1,4 +1,4 @@ -#include "mainwindow.h" +#include "mainwindow.h" #include #include #include @@ -200,6 +200,26 @@ void RegisterType(void) qDebug() << "register qml type"; } +QDateTime getCompileDateTime() { + // 1. 合并日期和时间宏 + QString rawDateTime = __DATE__ __TIME__; // 例如: "Feb 12 202512:34:56" + + // 2. 标准化天数格式:将月份和天数之间的单个空格替换为" 0" + // 目的是将 "Feb 12" 统一为 "Feb 12" (两个空格),将 "Feb 2" 统一为 "Feb 2" + #ifdef _MSC_VER + rawDateTime.replace(" ", " 0"); // 针对单数日期做处理 + #else + rawDateTime.replace(" ", " 0"); + // GCC/Clang 下 "Feb 2" 才会有两个空格 + rawDateTime.replace(" ", " 0"); + // MinGW 处理 + #endif + + // 3. 解析为标准 QDateTime + // 注意:格式 "MMM dd yyyyhh:mm:ss" 要求天数必须是 dd (两位数,含前导空格) + // 这里使用英文 locale 以确保月份名称被正确识别 + return QLocale(QLocale::English).toDateTime(rawDateTime, "MMM dd yyyyhh:mm:ss"); +} int main(int argc, char *argv[]) { @@ -223,7 +243,10 @@ int main(int argc, char *argv[]) MainWindow w;//这一步已经全部初始化完成 - w.setWindowTitle("KB-3A地面站软件_二院");//with version + + QDateTime buildTime = getCompileDateTime(); + + w.setWindowTitle(QString("KB-3A超音速靶机地面站软件_V%1").arg(buildTime.date().toString("yyyyMMdd")));//with version //w.setWindowTitle(QString("KB3A-V1.00 %1").arg(APP_VERSION));//with version // 主要是控制HDMI输出,如果是LCD显示,此行无关紧要 //w.resize(GetDesktopSize()); diff --git a/App/mainwindow.cpp b/App/mainwindow.cpp index 299a080..7dda531 100644 --- a/App/mainwindow.cpp +++ b/App/mainwindow.cpp @@ -1,4 +1,4 @@ -#include "mainwindow.h" +#include "mainwindow.h" #include "QPushButton" #include "QAction" @@ -191,6 +191,12 @@ MainWindow::MainWindow(QWidget *parent) connect(commandUI,SIGNAL(Accepted(bool,uint16_t,uint8_t)), toolsui->servosystem,SLOT(commandAccepted(bool,uint16_t,uint8_t)),Qt::DirectConnection); + connect(commandUI,SIGNAL(Accepted(bool,uint16_t,uint8_t)), + toolsui->payload,SLOT(commandAccepted(bool,uint16_t,uint8_t)),Qt::DirectConnection); + + connect(toolsui->payload,SIGNAL(cmd_long(float,float,float,float,float,float,float,uint16_t,uint8_t)), + dlink->mavlinknode->Commander,SLOT(WriteCmd_long(float,float,float,float,float,float,float,uint16_t,uint8_t)),Qt::DirectConnection); + //command ----- map connect(dlink->mavlinknode,SIGNAL(addVehicles(int,int)), @@ -598,8 +604,19 @@ MainWindow::MainWindow(QWidget *parent) connect(setting->index0->globalsetting,SIGNAL(showMessage(QString,int)),this,SLOT(showMessage(QString,int))); + connect(toolsui->payload,SIGNAL(showMessage(QString,int)),this,SLOT(showMessage(QString,int))); + connect(commandUI,&CommandUI::say,this,&MainWindow::TTSsay); + connect(toolsui->payload,&Payload::say,this,&MainWindow::TTSsay); + + + connect(dlink->mavlinknode,SIGNAL(enCapData(uint16_t,QByteArray)),toolsui->payload,SLOT(EnCapData(uint16_t,QByteArray)));//,Qt::DirectConnection + + connect(toolsui->payload,SIGNAL(send_EnCapData(int,QByteArray)), + dlink->mavlinknode->datstream,SLOT(sendData(int,QByteArray)),Qt::DirectConnection); + + //connect(menuBarUI,SIGNAL(NewMessage(QString)),toolsui->index1,SLOT(setLog(QString)),Qt::DirectConnection); qDebug() << "main window start"; @@ -1664,9 +1681,12 @@ void MainWindow::updateUI()//事件驱动式更新数据 //healthui->setState(7,getBit(health,29)?(getBit(health,28)?(HealthUI::state::success):(HealthUI::state::warning)):(HealthUI::state::failure));//空速管加热 healthui->setState(7,getBit(health,5)?(getBit(health,11)?(HealthUI::state::success):(HealthUI::state::warning)):(HealthUI::state::failure));//FuelLevel //8,9,10位为空 - healthui->setState(8,HealthUI::state::inital); - healthui->setState(9,HealthUI::state::inital); - healthui->setState(10,HealthUI::state::inital); + + uint8_t loadDevice_comm = vehicle.payloadalarmstate.loadDevice_comm; + + healthui->setState(8,getBit(loadDevice_comm,1)?(HealthUI::state::success):(HealthUI::state::failure)); + healthui->setState(9,getBit(loadDevice_comm,0)?(HealthUI::state::success):(HealthUI::state::failure)); + healthui->setState(10,getBit(loadDevice_comm,2)?(HealthUI::state::success):(HealthUI::state::failure)); toolsui->servosystem->setCheckState(6,getBit(health,22)?(HealthUI::state::failure):(HealthUI::state::success)); @@ -1917,6 +1937,9 @@ void MainWindow::updateUI()//事件驱动式更新数据 //qDebug() << "LAT" << present%10; //===================status ui ============================================= + uint32_t id = vehicle.sysid; + statusui->setCurrentPlane(id); + statusui->setMode(1,mode_str,0); statusui->setMode(2,AFCS_VERT,AFCS_LAT); @@ -2002,6 +2025,7 @@ void MainWindow::updateUI()//事件驱动式更新数据 qreal ru_command = dir_ru.toDouble() * ((int16_t)vehicle.servo_output_raw.servo13_raw)/32767.0 * max_ru.toDouble()/scale_ru.toDouble() + bias_ru.toDouble() * 57.295; qreal ru_angle = dir_ru.toDouble() * ((int16_t)vehicle.servo_output_raw.servo3_raw) /32767.0 * max_ru.toDouble()/scale_ru.toDouble() + bias_ru.toDouble() * 57.295; + /* qDebug() << la_command << la_angle << ra_command @@ -2012,6 +2036,7 @@ void MainWindow::updateUI()//事件驱动式更新数据 << re_angle << ru_command << ru_angle ; + */ statusui->setServo(1,QString::number(la_command,'f',2), @@ -2460,6 +2485,12 @@ void MainWindow::updateUI()//事件驱动式更新数据 //qDebug() << "nsecsElapsed" << ElapsedTimer.nsecsElapsed(); + + toolsui->payload->setUVWDState(vehicle.uvwdstate); + toolsui->payload->setRWRState(vehicle.rwrstate); + toolsui->payload->setDLSState(vehicle.dlsstate); + toolsui->payload->setPayloadStatus(vehicle.payloadalarmstate); + } void MainWindow::Timer_1s_out(void) diff --git a/MavLinkNode/MAVLINKNODE_zh_CN.ts b/MavLinkNode/MAVLINKNODE_zh_CN.ts index 321084e..cb3ca27 100644 --- a/MavLinkNode/MAVLINKNODE_zh_CN.ts +++ b/MavLinkNode/MAVLINKNODE_zh_CN.ts @@ -1,6 +1,14 @@ + + MavLinkNode + + + 出现新的设备,识别号为 %1 + + + MissionProcess diff --git a/MavLinkNode/MavLinkNode.pro b/MavLinkNode/MavLinkNode.pro index cd916dd..1df909a 100644 --- a/MavLinkNode/MavLinkNode.pro +++ b/MavLinkNode/MavLinkNode.pro @@ -53,6 +53,7 @@ HEADERS += \ Terminal.h \ ThreadTemplet.h \ commandprocess.h \ + datastream.h \ mavlinknode.h \ parameterprocess.h \ missionprocess.h \ @@ -68,6 +69,7 @@ SOURCES += \ Terminal.cpp \ ThreadTemplet.cpp \ commandprocess.cpp \ + datastream.cpp \ mavlinknode.cpp \ parameterprocess.cpp \ missionprocess.cpp \ diff --git a/MavLinkNode/commandprocess.cpp b/MavLinkNode/commandprocess.cpp index 33fb00a..129fa78 100644 --- a/MavLinkNode/commandprocess.cpp +++ b/MavLinkNode/commandprocess.cpp @@ -185,6 +185,7 @@ void commandprocess::WriteStateMachine(void) static uint8_t step = 0; static uint8_t timeout_count = 0; static int time = 0; + static QElapsedTimer timer; if(step == 0) { @@ -205,14 +206,20 @@ void commandprocess::WriteStateMachine(void) cmd_long.command,cmd_long.confirmation);//发送一个指令 } - time = QTime::currentTime().msecsSinceStartOfDay(); - step++;//下一个阶段 + //time = QTime::currentTime().msecsSinceStartOfDay(); + //step++;//下一个阶段 + + timer.start(); + step++; + } else if(step == 1)//等待返回 { - if((QTime::currentTime().msecsSinceStartOfDay() - time) > sendTimeout) + //if((QTime::currentTime().msecsSinceStartOfDay() - time) > sendTimeout) + if(timer.elapsed() > sendTimeout) { timeout_count ++; + step = 0;//返回上一个步骤 if(timeout_count > 0)//如果指令发1次还没发出去,那么结束发送 { diff --git a/MavLinkNode/datastream.cpp b/MavLinkNode/datastream.cpp new file mode 100644 index 0000000..7b7ae86 --- /dev/null +++ b/MavLinkNode/datastream.cpp @@ -0,0 +1,301 @@ +#include "DataStream.h" + +DataStream::DataStream(QObject *parent) : QObject(parent) +{ + running_flag = false; + thread = new QThread(); + this->moveToThread(thread); + thread->setPriority(QThread::NormalPriority); + + connect(thread, &QThread::started, this, &DataStream::process); + + setRunFrq(80); + + initbuff(); + start(); + + Parser2_init(&parser); + Packer2_init(&packer); +} + +DataStream::~DataStream() +{ + stop(); + if(thread) + { + thread->deleteLater(); + } +} + +void DataStream::setRunFrq(qreal frq) +{ + if((frq != 0)||(frq <= 1000)) + { + running_frq = frq; + qDebug() << "set running frquency:" <isRunning()) + { + running_flag = true; + thread->start(); + qDebug() << "thread start" << thread->isRunning(); + } + else + { + qDebug() << "thread has started"; + } +} + +void DataStream::stop() +{ + if(thread) + { + if(!thread->isFinished()) + { + running_flag = false; + thread->requestInterruption(); + thread->setPriority(QThread::HighPriority);//设置成最高,让线程优先退出 + + bool flag = disconnect(thread, nullptr, nullptr, nullptr);//没有完全断开 + + qDebug() << "disconnect" << flag << "isInterruptionRequested" << thread->isInterruptionRequested(); + + + thread->quit(); + thread->wait(1000);//这个地方有问题、没办法退出线程,断开之前,先断开串口连接,这样就能断开线程,否则一直在占用 + } + else + { + qDebug() << "thread is not running"; + } + } +} + +void DataStream::process()//线程函数 +{ + uint8_t count = 0; + while (true) + { + count ++; + QThread::msleep(1000.0/frq()); + + switch(status.m_Mode) + { + default: + case Nop_Mode : QThread::yieldCurrentThread();break; + case RecieveMode : break; + case TransmitMode : sendAFrame();status.m_Mode = Nop_Mode;break; + } + + if(isInterruptionRequested())//退出 + { + break; + } + } + +} + +void DataStream::setGCSID(int m_sysid, int m_compid) +{ + GCS_SysID = m_sysid; + GCS_CompID = m_compid; +} + +void DataStream::setID(int m_sysid,int m_compid) +{ + sysid = (uint8_t)m_sysid; + compid = (uint8_t)m_compid; +} + + +void DataStream::Send(mavlink_message_t msg) +{ + if(isActive()) + { + uint8_t buff[MAVLINK_MAX_PACKET_LEN+sizeof(quint64)]; + uint16_t len = mavlink_msg_to_send_buffer(buff, &msg); + + if(plogFileName.size() > 0) + { + setLogData(plogFileName,1,msg); + } + + emit SendMessageTo(0,buff, len);//使用信号和槽 + } +} + +bool DataStream::setLogData(QString name,uint8_t type,mavlink_message_t msg) +{ + mutex.lock(); + QFile file(name); + if(file.open(QIODevice::Append)) + { + uint8_t buff[MAVLINK_MAX_PACKET_LEN+sizeof(quint64)]; + qToLittleEndian(type, buff); + + + quint64 currentTimestamp = (quint64)QDateTime::currentMSecsSinceEpoch(); + qToLittleEndian(currentTimestamp, buff+1); + uint16_t len = mavlink_msg_to_send_buffer(buff+1+sizeof(quint64), &msg); + + auto size = Packer2_pack(&packer,1,buff,len+1+sizeof(quint64)); + + + QByteArray data; + data.clear(); + data.append((const char *)packer.buff,size); + + + QDataStream stream(&file); + + stream << data; + + /* + if (file.write((const char *)packer.buff, size) == size) + { + + } + */ + + file.flush(); + file.close(); + mutex.unlock(); + return true; + } + + mutex.unlock(); + return false; +} + + + + +void DataStream::initbuff(void) +{ + client_buff.max_size = 10 * 1024 *1024;//10M + client_buff.buff[0].clear(); + client_buff.buff[1].clear(); + client_buff.select = 0; + + serial_buff.max_size = 10 * 1024 *1024; + serial_buff.buff[0].clear(); + serial_buff.buff[1].clear(); + serial_buff.select = 0; +} + +void DataStream::setbuff(quint32 src,QByteArray data) +{ + switch (src) { + default: + case SourceType::c_sock: + //当前的buff超过10M字节之后就清除,防爆机制,不然buff太大后容易卡死 + if(client_buff.buff[client_buff.select].size() >= client_buff.max_size) + { + client_buff.buff[client_buff.select].clear(); + qDebug() << "client_buff.buff " << client_buff.select <<" OVERFLOW"; + } + client_buff.buff[client_buff.select].append(data); + + break; + case SourceType::s_port: + //当前的buff超过10M字节之后就清除,防爆机制,不然buff太大后容易卡死 + if(serial_buff.buff[serial_buff.select].size() >= serial_buff.max_size) + { + serial_buff.buff[serial_buff.select].clear(); + qDebug() << "serial_buff.buff " << serial_buff.select <<" OVERFLOW"; + } + serial_buff.buff[serial_buff.select].append(data); + break; + } +} + +QByteArray DataStream::readbuff(quint32 src) +{ + QByteArray datagram; + + switch (src) { + default: + case SourceType::c_sock: + if(client_buff.select == 0) + { + datagram.clear(); + datagram.append(client_buff.buff[1]); + //清除这个未选择的buff + client_buff.buff[1].clear(); + //读取完成,可以往这个内存里面写数了 + client_buff.select = 1; + } + else if(client_buff.select == 1) + { + datagram.clear(); + datagram.append(client_buff.buff[0]); + //清除这个未选择的buff + client_buff.buff[0].clear(); + //读取完成,可以往这个内存里面写数了 + client_buff.select = 0; + } + break; + case SourceType::s_port: + if(serial_buff.select == 0) + { + datagram.clear(); + datagram.append(serial_buff.buff[1]); + //读取完成,可以往这个内存里面写数了 + serial_buff.buff[1].clear(); + serial_buff.select = 1; + } + else if(serial_buff.select == 1) + { + datagram.clear(); + datagram.append(serial_buff.buff[0]); + //读取完成,可以往这个内存里面写数了 + serial_buff.buff[0].clear(); + serial_buff.select = 0; + } + break; + } + return datagram; +} + +//发送函数 +void DataStream::sendData(const int &id, const QByteArray &data) +{ + idCurrent = id; + databuff.clear(); + databuff.append(data); + status.m_Mode = TransmitMode; +} + +void DataStream::sendAFrame(void) +{ + //收到指令 + mavlink_message_t msg; + mavlink_encapsulated_data_t encapsulated_data; + + encapsulated_data.seqnr = idCurrent; + + size_t len = databuff.size(); + + if(len > 253) + { + len = 253; + } + + for(uint16_t i = 0;i < len;i++) + { + encapsulated_data.data[i] = (uint8_t)databuff[i]; + } + + qDebug() << "send encap" << databuff; + + mavlink_msg_encapsulated_data_encode(GCS_SysID,GCS_CompID, &msg,&encapsulated_data); + Send(msg); +} + + + + diff --git a/MavLinkNode/datastream.h b/MavLinkNode/datastream.h new file mode 100644 index 0000000..5841bb5 --- /dev/null +++ b/MavLinkNode/datastream.h @@ -0,0 +1,163 @@ +#ifndef DataStream_H +#define DataStream_H + +#include +#include "QThread" +#include "QFile" +#include "QDebug" +#include "mavlink.h" +#include "QtEndian" +#include "QDataStream" +#include "QFile" +#include "QDateTime" +#include "ParsePack.h" +#include "QMutex" + +#ifdef QtMavlinkNode +#include +class MAVLINKNODESHARED_EXPORT DataStream : public QObject { +#else +class DataStream : public QObject +{ +#endif + Q_OBJECT + +public: + explicit DataStream(QObject *parent = nullptr); + ~DataStream(); + + + +signals: + + Q_INVOKABLE void error(QVariant type); + + Q_INVOKABLE void SendMessageTo(quint8 ch, quint8 *data,quint16 len); + + Q_INVOKABLE void showMessage(const QString &message,int TimeOut = 0); + +public slots: + void sendData(const int &id, const QByteArray &data); + + void sendAFrame(void); + + + //地面站对外接口 + void setGCSID(int m_sysid, int m_compid); + void setID(int m_sysid,int m_compid); + + + + //线程对外接口 + void setRunFrq(qreal frq); + void start(); + void stop(); + bool isActive(void) + { + return running_flag; + } + + bool isInterruptionRequested(void) + { + return QThread::currentThread()->isInterruptionRequested(); + } + + float frq(void) + { + return running_frq; + } + + void setPlogName(QString name) + { + plogFileName = name; + } + + //缓存对外接口 + Q_INVOKABLE void setbuff(quint32 src,QByteArray data); + void initbuff(void); + QByteArray readbuff(quint32 src); + + + void Send(mavlink_message_t msg); + + bool setLogData(QString name, uint8_t type, mavlink_message_t msg); + +private slots: + //线程私有接口 + virtual void process(); + + //缓存私有接口 + + //解析 + +protected: + + typedef struct { + qint32 max_size; + quint8 select; + QByteArray buff[2]; + }_buffdef; + + enum SourceType{ + c_sock = 0, + s_port = 1 + }; + + enum _modetype { + Nop_Mode = 0, + RecieveMode, + TransmitMode + }; + + typedef struct { + bool isWaitingforValue; + }_recieve; + + typedef struct { + bool isWaitingforACK; + uint8_t type; + }_transmit; + + typedef struct + { + _recieve recieve; + _transmit transmit; + _modetype m_Mode; + }_status_; + + _status_ status; + + bool running_flag = false; + qreal running_frq = 0.01;//1Hz + + QThread *thread = nullptr; + + _buffdef serial_buff; + _buffdef client_buff; + + + int GCS_SysID = 0xF1; + int GCS_CompID = MAV_COMP_ID_MISSIONPLANNER; + + //目标id + uint8_t sysid; + uint8_t compid; + + Parser2_t parser; + Packer2_t packer; + + QString plogFileName; + + QMutex mutex; + + + int idCurrent = 0; + QByteArray databuff; + bool isSend = false; + + + + + +}; +#endif // DataStream_H diff --git a/MavLinkNode/mavlinknode.cpp b/MavLinkNode/mavlinknode.cpp index bbdb25f..7a9cb36 100644 --- a/MavLinkNode/mavlinknode.cpp +++ b/MavLinkNode/mavlinknode.cpp @@ -1,4 +1,4 @@ -#include "mavlinknode.h" +#include "mavlinknode.h" #include "QElapsedTimer" #include /* @@ -343,6 +343,20 @@ MavLinkNode::MavLinkNode(QObject *parent) : ThreadTemplet(parent) showInfoThread.start(); + + datstream = new DataStream(); + + datstream->setGCSID(Current_sysID,Current_CompID); + //datstream->setPlogName(plogFileName); + connect(datstream,&DataStream::SendMessageTo, + this,&MavLinkNode::SendMessageTo,Qt::DirectConnection); + + connect(this,SIGNAL(setCurrentID(int,int)), + datstream,SLOT(setID(int,int)),Qt::DirectConnection); + + connect(datstream,SIGNAL(showMessage(QString,int)), + this,SIGNAL(showMessage(QString,int)),Qt::DirectConnection); + } MavLinkNode::~MavLinkNode() @@ -990,11 +1004,14 @@ void MavLinkNode::timer_1s_Out(void) } } +//mavlink_message_t msg;// = void MavLinkNode::Mavlinkparse(quint32 src,QByteArray datagram) { static int count = 0; static uint8_t last_seq = 0; - mavlink_message_t msg; + mavlink_message_t msg;// = + + mavlink_status_t status; hasConneted = true; @@ -1120,8 +1137,74 @@ void MavLinkNode::Mavlinkparse(quint32 src,QByteArray datagram) }break; case MAVLINK_PARSE_STATE_GOT_BAD_CRC1:{ parserFailure += 1; - qDebug() << "MAVLINK_PARSE_STATE_GOT_BAD_CRC1"; - emit showMessage(u8"协议解码校验错误"); + qDebug() << "MAVLINK_PARSE_STATE_GOT_BAD_CRC1" << msg.msgid; + QString string("协议解码校验错误"); + + string.append(' '); + string.append(QString("%1").arg(msg.msgid)); + emit showMessage(string); + + + mavlink_message_t msg_payload; + mavlink_payloadalarmstate_t payloadalarmstate; + + payloadalarmstate.uvwd_alarm_flag = 2; + payloadalarmstate.rwr_alarm_flag = 0; /*< RWR alarm status: 0=no alarm; 1=threat detected; 2=alarm triggered (threat level >= set value)*/ + payloadalarmstate.loadDevice_comm = 6; /*< payload communication status: bit0=UV; bit1=radar; bit2=DLS.0:Normal;1:Error*/ + payloadalarmstate.RESERVED1 = 0; /*< RESERVED1*/ + payloadalarmstate.RESERVED2 = 0; /*< RESERVED2*/ + payloadalarmstate.RESERVED3 = 0; /*< RESERVED3*/ + payloadalarmstate.RESERVED4 = 0; /*< RESERVED4*/ + payloadalarmstate.RESERVED5 = 0; /*< RESERVED5*/ + payloadalarmstate.RESERVED6 = 0; /*< RESERVED6*/ + payloadalarmstate.RESERVED7 = 0; /*< RESERVED7*/ + payloadalarmstate.RESERVED8 = 0; /*< RESERVED8*/ + payloadalarmstate.RESERVED9 = 0; /*< RESERVED9*/ + payloadalarmstate.RESERVED10 = 0; + + mavlink_msg_payloadalarmstate_encode(1,1,&msg_payload,&payloadalarmstate); + + uint8_t buff[MAVLINK_MAX_PACKET_LEN+sizeof(quint64)]; + uint16_t len = mavlink_msg_to_send_buffer(buff, &msg_payload); + + QString str; + str.clear(); + for(int i = 0;i @@ -18,6 +18,7 @@ #include "rcprocess.h" #include "rtkprocess.h" +#include "DataStream.h" #include "ThreadTemplet.h" @@ -83,6 +84,12 @@ public: mavlink_ccmstate_t ccmstate = {0}; mavlink_serial_control_t serial_control = {0}; + mavlink_uvwdstate_t uvwdstate = {0}; + mavlink_rwrstate_t rwrstate = {0}; + mavlink_dlsstate_t dlsstate = {0}; + mavlink_payloadalarmstate_t payloadalarmstate = {0}; + + }_vehicle; typedef struct @@ -126,6 +133,7 @@ public: terminal *Terminal = nullptr; rcprocess *RC = nullptr; rtkprocess *rtk = nullptr; + DataStream *datstream = nullptr; bool isCommunicationLost = false; @@ -211,6 +219,11 @@ signals: void signal_ccmstate(mavlink_ccmstate_t); void signal_serial_control(mavlink_serial_control_t); + void signal_uvwdstate(mavlink_uvwdstate_t); + void signal_rwrstate(mavlink_rwrstate_t); + void signal_dlsstate(mavlink_dlsstate_t); + void signal_payloadalarmstate(mavlink_payloadalarmstate_t); + void signal_vehicle(MavLinkNode::_vehicle vehicle); void updateDlink(float rssi,uint64_t in,uint64_t out); @@ -240,6 +253,14 @@ signals: void SendMessageToExport(quint8 ch, quint8 *data,quint16 len); + + + + void recievePayloadData(QByteArray); + + void enCapData(uint16_t,QByteArray); + + public slots: /** * @brief 设置厂家标识 diff --git a/dlink/dlink.cpp b/dlink/dlink.cpp index e62d32e..37c1f68 100644 --- a/dlink/dlink.cpp +++ b/dlink/dlink.cpp @@ -1,4 +1,4 @@ -#include "dlink.h" +#include "dlink.h" #include #include "QDateTime" #include "QElapsedTimer" @@ -219,7 +219,7 @@ int DLink::SendMessageToExport(quint8 ch, quint8 *msg, quint16 len) qint64 flag = Exportsock->writeDatagram((const char *)msg,len,Export_addr, Export_port); if(flag != -1) { - qDebug() << "export Client Send Msg" << Export_addr <