add node recommendation

This commit is contained in:
baiYue 2025-12-19 18:28:13 +08:00
parent a2b45ca406
commit 37776d1258
33 changed files with 917 additions and 218 deletions

View File

@ -1373,6 +1373,14 @@ struct ModelTypeInfo{ //类型临时信息
QString sMeta; //该类型元模名
QString sModel; //该类型工程模名
};
/*******************传递的数据************************/
struct HttpRecommandInfo{ //推荐数据服务
QString sInput; //输入的前缀
int nOffset = 0; //正确位置计数
QStringList lstRecommand; //推荐列表
};
/**
* Constants used for fetching QVariant data from GraphModel.
*/

View File

@ -33,6 +33,8 @@ public slots:
void onDeleteClicked();
void onModifyClicked();
void onIndexRbtnClicked(const QPoint &pos); //索引列表右键菜单
void onHttpDataUpdated(HttpRecommandInfo);
private:
void initial();
private:

View File

@ -20,6 +20,7 @@ class CornerMonitorLauncher;
class LoadMonitorPageDlg;
class DiagramConnectSetting;
class DataAccessor;
struct HttpRecommandInfo;
class DIAGRAM_DESIGNER_PUBLIC DiagramCavas : public QMdiArea
{
@ -34,6 +35,8 @@ public:
MonitorPanel* getMonitorPanel(QString);
void updateSubPos();
void passRecommmandHttpData(HttpRecommandInfo); //传递推荐列表数据
DiagramConnectSetting* getConnectSettingDlg() {return _connectSetting;}
public:
void initial();
signals:

View File

@ -20,8 +20,11 @@ public:
~DiagramConnectSetting();
void showDlg();
void updateHttpLog(QString sType,QString data);
void updateWebsocketLog(QString);
public slots:
void onTestHttpClicked();
void onTestHttpRecommandClicked();
void onTestHttpDataClicked();
void onTestWebsocketClicked();
void onOkClicked();
void onCancelClicked();

View File

@ -32,6 +32,7 @@ class ModelProperty;
struct itemPageInfo;
class EditBaseItem;
class MonitorPanel;
class ItemPropertyDlg;
class FixedPortsModel : public BaseModel, public Serializable
{
@ -112,9 +113,15 @@ public:
void monitorItemSet(QUuid); //运行时item设置完成
void updateMonitorDisplay(); //更新监控图元显示
void startAcceptData(); //开始接收实时数据
void stopAcceptData(QString); //停止接收实时数据
QMap<int,QList<QPair<monitorItemState,QString>>>& getMonitorStateMap(){return m_monitorStateMap;}
void setMonitorDisplaySetting(QMap<monitorItemTypeStruct,QMap<monitorItemStateStruct,monitorItemDisplayInfo>> map){m_monitorDisplaySetting = map;}
QMap<monitorItemTypeStruct,QMap<monitorItemStateStruct,monitorItemDisplayInfo>>& getMonitorDisplaySetting(){return m_monitorDisplaySetting;}
/************************数据显示*************************/
void setCurItemPropertyDlg(ItemPropertyDlg* p) {m_curPropertyDlg = p;}
ItemPropertyDlg* getCurItemPropertyDlg() {return m_curPropertyDlg;}
Q_SIGNALS:
void activatePage(const QString&); //激活当前model所在page
void updateCurrentItems(QList<monitorRelationItem>,bool); //更新当前组态元件列表 <名称,uid>
@ -169,6 +176,8 @@ private:
QMap<int,QList<QPair<monitorItemState,QString>>> m_monitorStateMap; //元件状态对照表 <type,<state,状态名>>
QMap<monitorItemTypeStruct,QMap<monitorItemStateStruct,monitorItemDisplayInfo>> m_monitorDisplaySetting; //元件设置
QList<monitorRelationItem> m_lstMonitorRelation; //监控item层级关系
ItemPropertyDlg* m_curPropertyDlg;
public:
static bool _dataInitialised;
};

View File

@ -6,18 +6,25 @@
#include <QMap>
#include <QMutex>
class DiagramCavas;
class DataAccessor : public QObject
{
Q_OBJECT
public:
DataAccessor(QObject *parent = nullptr);
~DataAccessor();
void setParent(DiagramCavas* p) {_parentCavas = p;}
public slots:
void onReceiveHttpData(const QString& sType,const QVariant& data);
void onReceiveWebsocketData(const QVariant& data);
private:
QMap<QString,QMap<quint64,double>> _realTimeData;
QString removeAfterStreamBySplit(const QString& url); //手动处理websocket的config
private:
QMap<QString,QMap<quint64,double>> _realTimeData; //实时数据缓存 todo:自动清理
mutable QMutex m_mutex;
DiagramCavas* _parentCavas;
};
#endif

View File

@ -29,10 +29,13 @@ public:
void setModelController(FixedPortsModel* p) {_curModelController = p;}
auto getModelController() {return _curModelController;}
auto getCurItem() {return _curItem;}
public slots:
void onOkClicked();
void onCancelClicked();
void onGroupSelected(const QString&);
void onHttpDataUpdated(HttpRecommandInfo); //更新推荐列表
private:
Ui::itemPropertyDlg *ui;
@ -47,6 +50,7 @@ private:
QString _curModel; //当前模型名
GraphicsProjectModelItem* _curItem;
FixedPortsModel* _curModelController;
QString _curGroup; //当前属性组
};
#endif

View File

@ -12,6 +12,7 @@ QT_END_NAMESPACE
class BayInfoDlg;
struct MeasurementInfo;
struct HttpRecommandInfo;
class MeasureSettingDlg : public QDialog
{
@ -36,6 +37,8 @@ public slots:
void onAddParaClicked();
void onDelParaClicked();
void onEventStrategyChange(int); //事件策略改变
void onHttpDataUpdated(HttpRecommandInfo); //更新推荐对象列表(若有)
private:
void clearData();
private:

View File

@ -14,6 +14,8 @@ class QItemSelection;
class QStandardItemModel;
class QStandardItem;
struct monitorItemAttributeInfo;
class QCompleter;
class QStringListModel;
class MonitorConfigDlg : public QDialog
{
@ -24,6 +26,7 @@ public:
void initial();
void updateSelectedItems();
void updateRecommandLst(QStringList); //更新当前推荐列表
public slots:
void onOkClicked();
void onCancelClicked();
@ -32,6 +35,10 @@ public slots:
void onItemSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected); //选中设备事件
void onPropertyCheckChanged(QStandardItem *item); //属性勾选改变信号
void onPropertySelectionChanged(const QModelIndex &current, const QModelIndex &previous); //属性选中事件
void onConnectParamChanged(const QString&); //连接参数变化事件(发送推荐查询)
protected:
bool eventFilter(QObject *obj, QEvent *event) override;
private:
void savePropertyData(const QModelIndex &current,QUuid uid); //保存属性到uid的属性
void loadPropertyData(const QModelIndex &current,QUuid uid);
@ -42,6 +49,10 @@ private:
QMap<QUuid,QList<monitorItemAttributeInfo>> _tempConfig;
QStandardItemModel* _curItemModel;
QUuid _curUuid;
QStringList _curRecommandLst; //当前推荐列表
QCompleter* _recommandCompleter; //自动填充器
QStringListModel* _strLstModel; //自动填充模型
};
#endif

View File

@ -35,6 +35,8 @@ public:
void initDisplayState(); //初始化显示状态参照表
void initDisplaySetting(); //初始化显示状态设置
MonitorConfigDlg* getMonitorConfigDlg() {return _pConfigDlg;}
public:
//对层级关系的序列化与反序列化
QJsonArray serializeRelationToJsonArray(const QList<monitorRelationItem>& data) const;

View File

@ -443,3 +443,12 @@ void BayInfoDlg::onIndexRbtnClicked(const QPoint &pos)
// 在点击位置显示菜单
menu.exec(ui->tableWidget_local->mapToGlobal(pos));
}
void BayInfoDlg::onHttpDataUpdated(HttpRecommandInfo info)
{
if(_measureDlg){
if(_measureDlg->isVisible()){
}
}
}

View File

@ -23,6 +23,8 @@
#include "diagramCommunication/include/configManager.h"
#include "instance/dataAccessor.h"
#include "uiCommunicationBus.h"
#include "itemPropertyDlg.h"
#include "monitorConfigDlg.h"
DiagramCavas::DiagramCavas(QWidget *parent)
: QMdiArea(parent)
@ -66,6 +68,47 @@ void DiagramCavas::updateSubPos()
_cornerButton->positionAtCorner();
}
void DiagramCavas::passRecommmandHttpData(HttpRecommandInfo info)
{
QMdiSubWindow* pSub = currentSubWindow();
if(!pSub)
return;
QWidget* pWindow= pSub->widget();
BaseDrawingPanel* pPanel = dynamic_cast<BaseDrawingPanel*>(pWindow);
if(pPanel)
{
if(pPanel->getMode() == DM_run)
{
auto pMonitor = dynamic_cast<MonitorPanel*>(pPanel);
if(pMonitor){
auto pDlg = pMonitor->getMonitorConfigDlg();
if(pDlg){
QStringList lst;
for(auto &str:info.lstRecommand){
QString sCompleteName = info.sInput+str;
if(!lst.contains(sCompleteName))
lst.append(sCompleteName);
}
pDlg->updateRecommandLst(lst);
}
//FixedPortsModel* pModel = pMonitor->getModelController();
}
}
else if(pPanel->getMode() == DM_edit)
{
auto pDraw = dynamic_cast<DrawingPanel*>(pPanel);
if(pDraw){
FixedPortsModel* pModel = pDraw->getModelController();
ItemPropertyDlg* pDlg = pModel->getCurItemPropertyDlg();
if(pDlg){
pDlg->onHttpDataUpdated(info);
}
}
}
}
}
void DiagramCavas::initial()
{
//todo:读取数据并初始化
@ -99,6 +142,7 @@ void DiagramCavas::initial()
comm->updateWebSocketConfig(config->getWebSocketConfig());
_dataAccessor = new DataAccessor(this);
_dataAccessor->setParent(this);
connect(UiCommunicationBus::instance(),
SIGNAL(httpDataProcessed(QString,QVariant)),
_dataAccessor,

View File

@ -1,6 +1,8 @@
#include "diagramConnectSetting.h"
#include "ui_diagramConnectSetting.h"
#include "diagramCommunication/include/configManager.h"
#include "uiCommunicationBus.h"
#include "communicationManager.h"
DiagramConnectSetting::DiagramConnectSetting(QWidget *parent)
: QDialog(parent)
@ -18,7 +20,8 @@ DiagramConnectSetting::~DiagramConnectSetting()
void DiagramConnectSetting::initial()
{
connect(ui->btn_testHttp,&QPushButton::clicked,this,&DiagramConnectSetting::onTestHttpClicked);
connect(ui->btn_testRecommand,&QPushButton::clicked,this,&DiagramConnectSetting::onTestHttpRecommandClicked);
connect(ui->btn_testData,&QPushButton::clicked,this,&DiagramConnectSetting::onTestWebsocketClicked);
connect(ui->btn_testWebsoc,&QPushButton::clicked,this,&DiagramConnectSetting::onTestWebsocketClicked);
connect(ui->btn_ok,&QPushButton::clicked,this,&DiagramConnectSetting::onOkClicked);
connect(ui->btn_cancel,&QPushButton::clicked,this,&DiagramConnectSetting::onCancelClicked);
@ -49,7 +52,29 @@ void DiagramConnectSetting::showDlg()
updateByConfig(socketInfo,1);
}
void DiagramConnectSetting::onTestHttpClicked()
void DiagramConnectSetting::updateHttpLog(QString sType,QString data)
{
ui->lst_log->addItem("接收http:"+data);
if(sType == "recommend"){
ui->label_httpRecommand->setText("联通");
}
else if(sType == "subscriptions"){
ui->label_httpData->setText("联通");
}
}
void DiagramConnectSetting::updateWebsocketLog(QString str)
{
ui->lst_log->addItem("接收websocket:"+str);
}
void DiagramConnectSetting::onTestHttpRecommandClicked()
{
QString strUrl = ui->le_httpIp->text()+"/measurement/recommend?input=";
UiCommunicationBus::instance()->sendHttpRequest(strUrl);
}
void DiagramConnectSetting::onTestHttpDataClicked()
{
}
@ -61,6 +86,20 @@ void DiagramConnectSetting::onTestWebsocketClicked()
void DiagramConnectSetting::onOkClicked()
{
CommunicationManager* comm = CommunicationManager::instance();
ConfigManager* config = ConfigManager::instance();
// 应用配置
auto httpConfig = config->getHttpConfig();
auto websocketConfig = config->getWebSocketConfig();
httpConfig.endpoint = ui->le_httpIp->text();
httpConfig.timeout = ui->le_httpOver->text().toInt();
websocketConfig.endpoint = ui->le_websocIp->text();
websocketConfig.timeout = ui->le_websocOver->text().toInt();
websocketConfig.heartbeatInterval = ui->le_websocHeart->text().toInt();
comm->updateHttpConfig(httpConfig);
comm->updateWebSocketConfig(config->getWebSocketConfig());
hide();
}

View File

@ -47,6 +47,7 @@
#include "projectIconSetting.h"
#include "monitorPanel.h"
#include "designerView.h"
#include "uiCommunicationBus.h"
#include "global.h"
bool FixedPortsModel::_dataInitialised = false;
@ -59,6 +60,7 @@ FixedPortsModel::FixedPortsModel(PowerEntity* pEntity)
,m_proModelSettingDlg(nullptr)
,m_projectIconSettingDlg(nullptr)
,m_pBayManager(nullptr)
,m_curPropertyDlg(nullptr)
{
_cavas = nullptr;
loadNodeDataFromDataBase();
@ -1299,7 +1301,7 @@ QPointF FixedPortsModel::getTerminalPos(const QString& sTerminalId)
ElectricConnectLineItem* FixedPortsModel::getLineItemById(const QString& terminalId)
{
for(auto iter:_nodeItem) //获取id所在的lineitem
for(auto &iter:_nodeItem) //获取id所在的lineitem
{
auto item = dynamic_cast<GraphicsProjectModelItem*>(iter);
if(item)
@ -1326,6 +1328,7 @@ void FixedPortsModel::showModelDlg(const QString& sName,QUuid uuid,GraphicsProje
if(pDlg)
{
pDlg->showDlg(mapData[sName],uuid,pItem);
m_curPropertyDlg = pDlg;
}
else
qDebug()<<"showModelDlg err";
@ -2797,3 +2800,74 @@ void FixedPortsModel::updateMonitorDisplay()
}
}
}
void FixedPortsModel::startAcceptData()
{
QStringList lstTarget; //待订阅的对象
for(auto& lst:m_monitorPara){
for(auto& para:lst){
if(para.bSelected && !para.sConnectPara.isEmpty()){
if(!lstTarget.contains(para.sConnectPara))
lstTarget.append(para.sConnectPara);
}
}
}
QJsonObject obj; //构建开始请求
obj["action"] = "start";
QJsonArray arrMeasure;
QJsonObject objMeasure;
objMeasure["interval"] = "2s";
QJsonArray arrTargets;
for(auto& strTarget:lstTarget){
arrTargets.append(strTarget);
}
objMeasure["targets"] = arrTargets;
arrMeasure.append(objMeasure);
obj["measurements"] = arrMeasure;
QString sPath = "/monitors/data/subscriptions";
QJsonDocument doc(obj);
QVariant variant = doc.toVariant();
UiCommunicationBus::instance()->sendHttpRequest(sPath,variant,"POST");
QList<QPair<QString,QString>> requestLst; //等待请求的队列
for(auto& str:lstTarget){
requestLst.append(qMakePair(str,"request"));
}
UiCommunicationBus::instance()->insertTempRequest(_pageName,requestLst);
}
void FixedPortsModel::stopAcceptData(QString page)
{
if(UiCommunicationBus::instance()->getSesstionMap().contains(page)){
auto& curSession = UiCommunicationBus::instance()->getSesstionMap()[page];
QJsonObject obj; //构建开始请求
obj["action"] = "stop";
obj["client_id"] = curSession.first;
QJsonArray arrMeasure;
QJsonObject objMeasure;
objMeasure["interval"] = "2s";
QJsonArray arrTargets;
for(auto& pairTarget:curSession.second){
arrTargets.append(pairTarget.first);
pairTarget.second = "closing";
}
objMeasure["targets"] = arrTargets;
arrMeasure.append(objMeasure);
obj["measurements"] = arrMeasure;
QString sPath = "/monitors/data/subscriptions";
QJsonDocument doc(obj);
QVariant variant = doc.toVariant();
UiCommunicationBus::instance()->sendHttpRequest(sPath,variant,"POST");
}
}

View File

@ -1,10 +1,17 @@
#include "instance/dataAccessor.h"
#include "communicationManager.h"
#include "uiCommunicationBus.h"
#include "configManager.h"
#include "diagramCavas.h"
#include "diagramConnectSetting.h"
#include <QVariant>
#include <QJsonObject>
#include <QJsonArray>
#include "global.h"
DataAccessor::DataAccessor(QObject* parent)
: QObject(parent)
,_parentCavas(nullptr)
{
}
@ -17,28 +24,120 @@ DataAccessor::~DataAccessor()
void DataAccessor::onReceiveHttpData(const QString& sType,const QVariant& data)
{
if(sType == "subscriptions"){
QMap<QString,QList<QPair<QString,QString>>>& tempRequest = UiCommunicationBus::instance()->getTempRequestMap();
QJsonObject dataObj = data.toJsonObject();
QString sClientId = dataObj.value("client_id").toString();
QMap<QString,QString> lstRequest;
QMap<QString,QString> lstTarget;
QJsonArray measureArr = dataObj.value("measurements").toArray();
for(const QJsonValue& value : measureArr){
QJsonObject obj = value.toObject();
QString sId = obj["id"].toString();
QString sCode = obj["code"].toString();
if(sId == "1001"){
if(!lstRequest.contains(sId)){
lstRequest.insert(sId,sCode);
if(!lstTarget.contains(sId)){
lstTarget.insert(sId,sCode);
}
}
QString sAction;
auto mapSesstion = UiCommunicationBus::instance()->getSesstionMap();
bool bClientExist = false;
for(auto& session:mapSesstion){
if(session.first == sClientId){ //在会话列表中已存在是stop(暂只使用stop和start)
bClientExist = true;
sAction = "stop";
break;
}
}
if(sAction.isEmpty()){ //不是stop的情况
QStringList lstKeys = lstTarget.keys();
for(auto it = tempRequest.begin(); it != tempRequest.end(); ++it){
const QString& page = it.key();
QList<QPair<QString, QString>>& tempList = it.value();
// 从 tempList 提取所有 first 元素
QStringList firstElements;
firstElements.reserve(tempList.size());
for (const auto& pair : tempList)
{
firstElements.append(pair.first);
}
// 对两个列表进行排序(因为 moveMatchingRequests 内部会排序比较)
QStringList sortedFirstElements = firstElements;
QStringList sortedLstKeys = lstKeys;
std::sort(sortedFirstElements.begin(), sortedFirstElements.end());
std::sort(sortedLstKeys.begin(), sortedLstKeys.end());
// 比较两个列表是否相同
if (sortedFirstElements == sortedLstKeys)
{
// 这里需要一个 id 参数,你可以从 lstRequest 中获取或使用其他方式
QString id; // 你需要确定 id 从哪里来
// 调用 moveMatchingRequests
for(auto& pair:tempList){
pair.second = "connecting";
}
UiCommunicationBus::instance()->insertSesstionMap(id, lstTarget);
sAction = "start";
break;
}
}
}
/*QString input = dataObj.value("input").toString();
int offSet = dataObj.value("offset").toInt();
QJsonArray recommendedList = dataObj.value("recommended_list").toArray();
for(const QJsonValue& value : recommendedList){
QString content = value.toString();
}*/
if(!lstTarget.isEmpty()){
if(sAction == "start"){
auto config = ConfigManager::instance()->getWebSocketConfig();
QString sPre = removeAfterStreamBySplit(config.endpoint); //手动移除
config.endpoint = sPre + "/" + sClientId;
CommunicationManager::instance()->updateWebSocketConfig(config,sClientId);
CommunicationManager::instance()->connectWebSocket(sClientId);
}
else if(sAction == "stop"){ //已经停止完毕从session中移除会话
auto &map = UiCommunicationBus::instance()->getSesstionMap();
for(auto iter = map.begin();iter != map.end();++iter){
if(iter->first == sClientId){
iter = map.erase(iter);
break;
}
}
CommunicationManager::instance()->disconnectWebSocket(sClientId);
CommunicationManager::instance()->removeChannel(sClientId);
}
}
}
else if(sType == "recommend"){
QJsonArray dataArr = data.toJsonArray();
for(const QJsonValue& value:dataArr){
QJsonObject dataObj = value.toObject();
QString input = dataObj.value("input").toString();
int offSet = dataObj.value("offset").toInt();
QJsonArray recommendedList = dataObj.value("recommended_list").toArray();
HttpRecommandInfo info;
for(const QJsonValue& value : recommendedList){
QString content = value.toString();
info.lstRecommand.append(content);
}
info.sInput = input.left(offSet);
info.nOffset = offSet;
if(_parentCavas){
_parentCavas->passRecommmandHttpData(info);
}
}
}
if(_parentCavas){
auto pDlg = _parentCavas->getConnectSettingDlg();
if(pDlg){
QJsonObject jsonObj = data.value<QJsonObject>();
QJsonDocument doc(jsonObj);
QString compactJson = doc.toJson(QJsonDocument::Compact);
pDlg->updateHttpLog(sType,compactJson);
}
}
}
@ -51,17 +150,52 @@ void DataAccessor::onReceiveWebsocketData(const QVariant& data)
QJsonObject targetObj = value.toObject();
QString targetId = targetObj["id"].toString();
QJsonArray arrData = targetObj["datas"].toArray();
QMap<quint64, double> newInnerMap;
for (const QJsonValue& data : arrData){
QJsonObject dataObj = data.toObject();
QString sTime = dataObj["time"].toString();
double dVal = dataObj["value"].toDouble();
bool ok = false;
quint64 value = sTime.toULongLong(&ok);
if (ok) {
// 使用 value
newInnerMap.insert(value,dVal);
}
double dVal = dataObj["value"].toDouble();
}
QMutexLocker locker(&m_mutex);
if (_realTimeData.contains(targetId)) {
_realTimeData[targetId].insert(newInnerMap);
} else {
_realTimeData.insert(targetId, newInnerMap);
}
}
}
QMutexLocker locker(&m_mutex);
if(_parentCavas){
auto pDlg = _parentCavas->getConnectSettingDlg();
if(pDlg){
QJsonObject jsonObj = data.value<QJsonObject>();
QJsonDocument doc(jsonObj);
QString compactJson = doc.toJson(QJsonDocument::Compact);
pDlg->updateWebsocketLog(compactJson);
}
}
}
QString DataAccessor::removeAfterStreamBySplit(const QString& url)
{
QStringList parts = url.split('/');
QStringList resultParts;
for (int i = 0; i < parts.size(); i++) {
if (parts[i] == "stream") {
// 找到 "stream" 后,停止添加后续部分
resultParts.append("stream");
break;
}
resultParts.append(parts[i]);
}
return resultParts.join('/');
}

View File

@ -7,7 +7,7 @@
#include "ui_itemPropertyDlg.h"
#include "baseContentDlg.h"
#include "baseInfoDlg.h"
//#include "topologyManager.h"
#include "graphicsDataModel/fixedPortsModel.h"
#include "basePropertyManager.h"
#include "baseProperty.h"
#include "ptExtraInfoDlg.h"
@ -131,12 +131,16 @@ void ItemPropertyDlg::onOkClicked()
}
}
}
_curGroup = "";
_curItem = nullptr;
_curModelController->setCurItemPropertyDlg(nullptr);
hide();
}
void ItemPropertyDlg::onCancelClicked()
{
_curGroup = "";
_curModelController->setCurItemPropertyDlg(nullptr);
hide();
}
@ -165,6 +169,7 @@ void ItemPropertyDlg::onGroupSelected(const QString& str)
}
ui->stackedWidget->setCurrentWidget(groupViews_[str]);
_curGroup = str;
}
@ -203,6 +208,17 @@ void ItemPropertyDlg::createGroupView(const QString& str)
ui->stackedWidget->addWidget(contentDlg);
}
void ItemPropertyDlg::onHttpDataUpdated(HttpRecommandInfo info)
{
if(_curGroup == "bay"){
auto pWidget = groupViews_[_curGroup];
auto pBay = dynamic_cast<BayInfoDlg*>(pWidget);
if(pBay){
pBay->onHttpDataUpdated(info);
}
}
}
void ItemPropertyDlg::showDlg(modelDataInfo dataInfo,QUuid uuid,GraphicsProjectModelItem* pItem)
{
groupValue_ = dataInfo.groupInfo;

View File

@ -361,3 +361,8 @@ void MeasureSettingDlg::onEventStrategyChange(int n)
ui->sw_event->setCurrentIndex(1);
}
}
void MeasureSettingDlg::onHttpDataUpdated(HttpRecommandInfo info)
{
//
}

View File

@ -1,6 +1,10 @@
#include "monitorConfigDlg.h"
#include "ui_monitorConfigDlg.h"
#include <QCompleter>
#include <QStringListModel>
#include <QKeyEvent>
#include "monitorPanel.h"
#include "uiCommunicationBus.h"
#include "global.h"
MonitorConfigDlg::MonitorConfigDlg(QWidget *parent)
@ -8,6 +12,8 @@ MonitorConfigDlg::MonitorConfigDlg(QWidget *parent)
, ui(new Ui::monitorConfigDlg)
,_parent(nullptr)
,_curItemModel(nullptr)
,_recommandCompleter(nullptr)
,_strLstModel(nullptr)
{
ui->setupUi(this);
this->setWindowFlags(Qt::FramelessWindowHint | windowFlags());
@ -36,13 +42,44 @@ void MonitorConfigDlg::initial()
connect(_curItemModel,&QStandardItemModel::itemChanged, this,&MonitorConfigDlg::onPropertyCheckChanged);
connect(ui->treeView_para->selectionModel(), &QItemSelectionModel::currentChanged,
this, &MonitorConfigDlg::onPropertySelectionChanged);
_recommandCompleter = new QCompleter(this);
_recommandCompleter->setCaseSensitivity(Qt::CaseInsensitive);
_recommandCompleter->setFilterMode(Qt::MatchContains);
_recommandCompleter->setCompletionMode(QCompleter::PopupCompletion);
_strLstModel = new QStringListModel(this);
_recommandCompleter->setModel(_strLstModel);
ui->le_query->setCompleter(_recommandCompleter);
ui->le_query->installEventFilter(this);
ui->le_query->setPlaceholderText("输入空格获取初始值");
connect(ui->le_query, &QLineEdit::textChanged, this, [=](const QString &text) {
if (text.endsWith(".")) {
onConnectParamChanged(text);
}
});
//connect(ui->le_query,&QLineEdit::textChanged, this,&MonitorConfigDlg::onConnectParamChanged);
}
void MonitorConfigDlg::updateSelectedItems()
{
ui->treeView_item->expandAll();
_tempConfig = _parent->getModelController()->getMonitorPara();
int a = 1;
}
void MonitorConfigDlg::updateRecommandLst(QStringList lst)
{
for(auto& newName:lst){
if(!newName.isEmpty()){
if(!_curRecommandLst.contains(newName)){
_curRecommandLst.append(newName);
}
}
}
_strLstModel->setStringList(_curRecommandLst);
if (_recommandCompleter->popup()->isVisible()) {
_recommandCompleter->popup()->hide();
}
_recommandCompleter->complete(); // 重新显示补全列表
}
void MonitorConfigDlg::onOkClicked()
@ -147,6 +184,33 @@ void MonitorConfigDlg::onPropertySelectionChanged(const QModelIndex &current, co
}
}
void MonitorConfigDlg::onConnectParamChanged(const QString& str)
{
QVariantMap map;
map.insert("input",str);
UiCommunicationBus::instance()->sendHttpRequest("/measurement/recommend",QVariant(),"GET",map);
}
bool MonitorConfigDlg::eventFilter(QObject *obj, QEvent *event)
{
if (obj == ui->le_query && event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
if (keyEvent->key() == Qt::Key_Space) {
qDebug() << "eventFilter:space";
onConnectParamChanged(ui->le_query->text());
_recommandCompleter->complete();
return true;
}
if (keyEvent->text() == ".") {
qDebug() << "eventFilter:dot";
// 返回false让事件继续传播
}
}
return QDialog::eventFilter(obj, event);
}
void MonitorConfigDlg::savePropertyData(const QModelIndex &previous,QUuid uid)
{
if(_tempConfig.contains(uid)){

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>393</width>
<height>502</height>
<width>414</width>
<height>588</height>
</rect>
</property>
<property name="font">
@ -98,12 +98,19 @@ font: 12pt &quot;Microsoft YaHei UI&quot;;</string>
<string>HTTP服务器配置</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="horizontalSpacing">
<number>4</number>
</property>
<property name="verticalSpacing">
<number>8</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>连接名称:</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="3">
<widget class="QLineEdit" name="le_httpName"/>
</item>
<item row="2" column="2">
<widget class="QLabel" name="label_6">
<property name="text">
@ -118,49 +125,6 @@ font: 12pt &quot;Microsoft YaHei UI&quot;;</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="3">
<widget class="QLineEdit" name="le_httpIp"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>连接名称:</string>
</property>
</widget>
</item>
<item row="2" column="3">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>217</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1" colspan="3">
<widget class="QLineEdit" name="le_httpName"/>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="le_httpOver">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>服务器地址</string>
</property>
</widget>
</item>
<item row="3" column="3">
<spacer name="verticalSpacer">
<property name="orientation">
@ -174,6 +138,39 @@ font: 12pt &quot;Microsoft YaHei UI&quot;;</string>
</property>
</spacer>
</item>
<item row="1" column="1" colspan="3">
<widget class="QLineEdit" name="le_httpIp"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>服务器地址:</string>
</property>
</widget>
</item>
<item row="2" column="3">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>217</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="le_httpOver">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@ -182,15 +179,15 @@ font: 12pt &quot;Microsoft YaHei UI&quot;;</string>
<property name="title">
<string>连接测试</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QPushButton" name="btn_testHttp">
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<widget class="QPushButton" name="btn_testRecommand">
<property name="text">
<string>测试连接</string>
<string>测试节点推荐</string>
</property>
</widget>
</item>
<item>
<item row="0" column="1" rowspan="2">
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
@ -203,21 +200,21 @@ font: 12pt &quot;Microsoft YaHei UI&quot;;</string>
</property>
</spacer>
</item>
<item>
<item row="0" column="2">
<widget class="QLabel" name="label_4">
<property name="text">
<string>状态:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_httpState">
<item row="0" column="3">
<widget class="QLabel" name="label_httpRecommand">
<property name="text">
<string>未连接</string>
</property>
</widget>
</item>
<item>
<item row="0" column="4" rowspan="2">
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
@ -230,6 +227,27 @@ font: 12pt &quot;Microsoft YaHei UI&quot;;</string>
</property>
</spacer>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="btn_testData">
<property name="text">
<string>测试数据服务</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="label_16">
<property name="text">
<string>状态:</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QLabel" name="label_httpData">
<property name="text">
<string>未连接</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>641</width>
<height>504</height>
<width>673</width>
<height>506</height>
</rect>
</property>
<property name="font">
@ -103,7 +103,7 @@ font: 12pt &quot;Microsoft YaHei UI&quot;;</string>
<attribute name="title">
<string>监控参数配置</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_4" columnstretch="2,2,3">
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
@ -131,18 +131,12 @@ font: 12pt &quot;Microsoft YaHei UI&quot;;</string>
</layout>
</widget>
</item>
<item row="0" column="2">
<item row="1" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>配置详情</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<property name="horizontalSpacing">
<number>6</number>
</property>
<property name="verticalSpacing">
<number>10</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
@ -150,7 +144,7 @@ font: 12pt &quot;Microsoft YaHei UI&quot;;</string>
</property>
</widget>
</item>
<item row="0" column="1">
<item row="0" column="1" colspan="2">
<widget class="QLineEdit" name="le_name"/>
</item>
<item row="1" column="0">
@ -160,8 +154,14 @@ font: 12pt &quot;Microsoft YaHei UI&quot;;</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="le_query"/>
<item row="1" column="1" colspan="2">
<widget class="QLineEdit" name="le_query">
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_4">
@ -172,6 +172,12 @@ font: 12pt &quot;Microsoft YaHei UI&quot;;</string>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="cb_type">
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<item>
<property name="text">
<string>字符</string>
@ -184,7 +190,20 @@ font: 12pt &quot;Microsoft YaHei UI&quot;;</string>
</item>
</widget>
</item>
<item row="3" column="0" colspan="2">
<item row="2" column="2">
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>526</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="0" colspan="3">
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>0</number>
@ -206,13 +225,6 @@ font: 12pt &quot;Microsoft YaHei UI&quot;;</string>
<property name="verticalSpacing">
<number>10</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>接线图中展示:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="cb_outVisible">
<item>
@ -240,6 +252,26 @@ font: 12pt &quot;Microsoft YaHei UI&quot;;</string>
</property>
</spacer>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>接线图中展示:</string>
</property>
</widget>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_2">

View File

@ -36,16 +36,17 @@ public:
QString channelId() const { return m_config.channelId; }
ChannelConfig config() const { return m_config; }
QUrl endpoint() const { return m_config.endpoint; }
QString sessionId() const {return m_sessionId;}
// 控制
void setAutoReconnect(bool enable);
bool isAutoReconnect() const { return m_autoReconnect; }
signals:
void connected();
void disconnected();
void dataReceived(const QByteArray& data);
void errorOccurred(const QString& error);
void connected(const QString& = "");
void disconnected(const QString& = "");
void dataReceived(const QByteArray& data,const QString& = "");
void errorOccurred(const QString& error,const QString& = "");
protected:
// 公共方法
@ -60,6 +61,7 @@ protected:
// 成员变量
ChannelConfig m_config;
bool m_autoReconnect = true;
QString m_sessionId; //会话idhttp无
private slots:
void onReconnectTimeout();

View File

@ -23,23 +23,26 @@ public:
bool disconnectHttp();
bool sendHttpRequest(const QString& path,
const QByteArray& data = QByteArray(),
const QString& method = "GET");
const QString& method = "GET",
const QVariantMap& query = QVariantMap());
// WebSocket通道操作
bool connectWebSocket();
bool disconnectWebSocket();
bool sendWebSocketMessage(const QByteArray& data);
bool sendWebSocketText(const QString& text);
bool connectWebSocket(const QString& sessionId = "");
bool disconnectWebSocket(const QString& sessionId = "");
bool removeChannel(const QString& sessionId);
bool sendWebSocketMessage(const QByteArray& data,const QString& sessionId);
bool sendWebSocketText(const QString& text,const QString& sessionId);
// 状态查询
bool isHttpConnected() const;
bool isWebSocketConnected() const;
bool isWebSocketConnected(const QString& sessionId) const;
ChannelConfig getHttpConfig() const;
ChannelConfig getWebSocketConfig() const;
// 配置更新
void updateHttpConfig(const ChannelConfig& config);
void updateWebSocketConfig(const ChannelConfig& config);
void updateWebSocketConfig(const ChannelConfig& config,const QString& sessionId = "");
signals:
// HTTP通道信号
@ -61,7 +64,7 @@ private:
// 内部初始化
void initHttpChannel();
void initWebSocketChannel();
void initWebSocketChannel(QString sessionId = ""); //服务器回传的会话id
// 配置
ChannelConfig m_httpConfig;
@ -69,5 +72,5 @@ private:
// 通道实例
QSharedPointer<HttpChannel> m_httpChannel;
QSharedPointer<WebSocketChannel> m_websocketChannel;
QMap<QString,QSharedPointer<WebSocketChannel>> m_websocketChannelMap; //<sessionId,websocketChannel>
};

View File

@ -32,6 +32,7 @@ private:
DataProcessor(QObject* parent = nullptr);
// 通用处理函数
void processJson(const QVariant& data,int conType = 0); //0http 1websocket
void processJsonArray(const QVariant& data);
// 数据缓存
QMap<QString, QVariant> m_dataCache;

View File

@ -16,7 +16,7 @@ public:
bool send(const QByteArray& data) override;
// HTTP方法
bool get(const QString& path = "");
bool get(const QString& path = "",const QVariantMap& queryMap = QVariantMap());
bool post(const QByteArray& data, const QString& path = "");
bool put(const QByteArray& data, const QString& path = "");
bool deleteResource(const QString& path = "");

View File

@ -16,7 +16,7 @@ public:
static UiCommunicationBus* instance();
// 发送HTTP请求
void sendHttpRequest(const QString& endpoint, const QVariant& data = QVariant());
void sendHttpRequest(const QString& endpoint, const QVariant& data = QVariant(),const QString& method = "GET",const QVariantMap& = QVariantMap());
// 发送HTTP请求无回复
void sendHttpRequestNoReply(const QString& endpoint, const QVariant& data = QVariant());
@ -30,6 +30,79 @@ public:
// 注册/注销UI
void registerUi(const QString& uiId, QObject* uiObject);
void unregisterUi(const QString& uiId);
QMap<QString,QList<QPair<QString,QString>>>& getTempRequestMap() {return _tempRequest;}
void insertTempRequest(QString page,QList<QPair<QString,QString>> lst){
if(!_tempRequest.contains(page)){
_tempRequest.insert(page,lst);
}
}
QMap<QString,QPair<QString,QList<QPair<QString,QString>>>>& getSesstionMap() {return _session;}
void insertSesstionMap(QString id,QMap<QString,QString> targetMap){ //从临时列表移除,插入到会话列表
QStringList sortedTargetList; //排序后的target列表
for(auto it = targetMap.begin();it != targetMap.end();++it){
sortedTargetList.append(it.key());
}
sortedTargetList.sort();
auto it = _tempRequest.begin();
while (it != _tempRequest.end())
{
const QString& page = it.key();
QList<QPair<QString, QString>>& tempList = it.value();
// 提取当前 tempList 中所有 first 元素组成的列表
QStringList firstElements;
firstElements.reserve(tempList.size());
for (const auto& pair : tempList)
{
firstElements.append(pair.first);
}
// 先进行简单检查:如果元素数量不同,肯定不匹配
if (firstElements.size() != sortedTargetList.size())
{
++it;
continue;
}
// 排序 firstElements
QStringList sortedFirstElements = firstElements;
sortedFirstElements.sort();
// 比较排序后的列表
if (sortedFirstElements == sortedTargetList)
{
// 匹配成功
// 检查 _session 中是否已有此 page
if (!_session.contains(page))
{
QList<QPair<QString, QString>> newList;
for (const auto& item : tempList) { //如果订阅不成功,从候选列表中移除 todo:记录错误target
auto it = targetMap.find(item.first);
if (it != targetMap.end()) {
if (it.value() == "1001") {
continue; // 跳过
}
}
newList.append(item);
}
tempList = newList;
// _session 中没有此 page可以插入
_session[page] = qMakePair(id, tempList);
}
// 无论 _session 中是否有此 page都从 _tempRequest 删除 *********同一个page中只存在一个会话******
it = _tempRequest.erase(it);
}
else
{
++it;
}
}
}
signals:
void httpDataProcessed(const QString& type,const QVariant& data); //发送分拣过的数据给外部
void websocketDataProcessed(const QVariant& data);
@ -45,4 +118,6 @@ private:
// UI注册表
QMap<QString, QObject*> m_uiObjects;
mutable QMutex m_mutex;
QMap<QString,QList<QPair<QString,QString>>> _tempRequest; //临时请求队列,一个QList为一次会话 <图名<节点名,状态>>
QMap<QString,QPair<QString,QList<QPair<QString,QString>>>> _session; //会话队列 <图名<会话id<节点名,状态>>>
};

View File

@ -13,7 +13,7 @@ public:
int heartbeatInterval = 30000;
};
WebSocketChannel(const ChannelConfig& config, QObject* parent = nullptr);
WebSocketChannel(const ChannelConfig& config, QString sessionId,QObject* parent = nullptr);
bool connect() override;
bool disconnect() override;
@ -24,14 +24,13 @@ public:
void setWebSocketConfig(const WebSocketConfig& config);
signals:
void textMessageReceived(const QString& message);
void textMessageReceived(const QString& message,const QString& = "");
private slots:
void onConnected();
void onDisconnected();
void onTextMessageReceived(const QString& message);
void onBinaryMessageReceived(const QByteArray& message);
private:
QWebSocket* m_webSocket = nullptr;
WebSocketConfig m_wsConfig;

View File

@ -41,7 +41,7 @@ bool CommunicationManager::initialize()
initHttpChannel();
// 初始化WebSocket通道
initWebSocketChannel();
//initWebSocketChannel();
qInfo() << "CommunicationManager initialized";
return true;
@ -81,13 +81,8 @@ void CommunicationManager::initHttpChannel()
this, &CommunicationManager::httpError);
}
void CommunicationManager::initWebSocketChannel()
void CommunicationManager::initWebSocketChannel(QString sessionId)
{
if (m_websocketChannel) {
m_websocketChannel->disconnect();
}
// 创建WebSocket通道
WebSocketChannel::ChannelConfig wsConfig;
wsConfig.endpoint = QUrl(m_websocketConfig.endpoint);
wsConfig.timeout = m_websocketConfig.timeout;
@ -95,20 +90,63 @@ void CommunicationManager::initWebSocketChannel()
WebSocketChannel::WebSocketConfig websocketConfig;
websocketConfig.heartbeatInterval = m_websocketConfig.heartbeatInterval;
m_websocketChannel.reset(new WebSocketChannel(wsConfig));
m_websocketChannel->setWebSocketConfig(websocketConfig);
if(sessionId.isEmpty()){ //没有指定会话初始化已有channel
for(auto& pChannel:m_websocketChannelMap){
pChannel->disconnect();
pChannel.reset(new WebSocketChannel(wsConfig,pChannel->sessionId()));
pChannel->setWebSocketConfig(websocketConfig);
// 连接信号
connect(m_websocketChannel.data(), &WebSocketChannel::connected,
this, &CommunicationManager::websocketConnected);
connect(m_websocketChannel.data(), &WebSocketChannel::disconnected,
this, &CommunicationManager::websocketDisconnected);
connect(m_websocketChannel.data(), &WebSocketChannel::dataReceived,
this, &CommunicationManager::websocketDataReceived);
connect(m_websocketChannel.data(), &WebSocketChannel::errorOccurred,
this, &CommunicationManager::websocketError);
connect(m_websocketChannel.data(), &WebSocketChannel::textMessageReceived,
this, &CommunicationManager::websocketTextReceived);
// 连接信号
connect(pChannel.data(), &WebSocketChannel::connected,
this, &CommunicationManager::websocketConnected);
connect(pChannel.data(), &WebSocketChannel::disconnected,
this, &CommunicationManager::websocketDisconnected);
connect(pChannel.data(), &WebSocketChannel::dataReceived,
this, &CommunicationManager::websocketDataReceived);
connect(pChannel.data(), &WebSocketChannel::errorOccurred,
this, &CommunicationManager::websocketError);
connect(pChannel.data(), &WebSocketChannel::textMessageReceived,
this, &CommunicationManager::websocketTextReceived);
}
}
else{
for(auto& pChannel:m_websocketChannelMap){
if(pChannel->sessionId() == sessionId){ //已存在
pChannel->disconnect();
pChannel.reset(new WebSocketChannel(wsConfig,pChannel->sessionId()));
pChannel->setWebSocketConfig(websocketConfig);
// 连接信号
connect(pChannel.data(), &WebSocketChannel::connected,
this, &CommunicationManager::websocketConnected);
connect(pChannel.data(), &WebSocketChannel::disconnected,
this, &CommunicationManager::websocketDisconnected);
connect(pChannel.data(), &WebSocketChannel::dataReceived,
this, &CommunicationManager::websocketDataReceived);
connect(pChannel.data(), &WebSocketChannel::errorOccurred,
this, &CommunicationManager::websocketError);
connect(pChannel.data(), &WebSocketChannel::textMessageReceived,
this, &CommunicationManager::websocketTextReceived);
return;
}
}
QSharedPointer<WebSocketChannel> newChannel(new WebSocketChannel(wsConfig,sessionId));
newChannel->setWebSocketConfig(websocketConfig);
// 连接信号
connect(newChannel.data(), &WebSocketChannel::connected,
this, &CommunicationManager::websocketConnected);
connect(newChannel.data(), &WebSocketChannel::disconnected,
this, &CommunicationManager::websocketDisconnected);
connect(newChannel.data(), &WebSocketChannel::dataReceived,
this, &CommunicationManager::websocketDataReceived);
connect(newChannel.data(), &WebSocketChannel::errorOccurred,
this, &CommunicationManager::websocketError);
connect(newChannel.data(), &WebSocketChannel::textMessageReceived,
this, &CommunicationManager::websocketTextReceived);
m_websocketChannelMap.insert(sessionId,newChannel);
}
}
bool CommunicationManager::connectHttp()
@ -140,7 +178,8 @@ bool CommunicationManager::disconnectHttp()
bool CommunicationManager::sendHttpRequest(const QString& path,
const QByteArray& data,
const QString& method)
const QString& method,
const QVariantMap& query)
{
if (!m_httpChannel || !m_httpChannel->isConnected()) {
qWarning() << "HTTP channel not connected";
@ -148,7 +187,7 @@ bool CommunicationManager::sendHttpRequest(const QString& path,
}
if (method == "GET") {
return m_httpChannel->get(path);
return m_httpChannel->get(path,query);
} else if (method == "POST") {
return m_httpChannel->post(data, path);
} else if (method == "PUT") {
@ -161,51 +200,108 @@ bool CommunicationManager::sendHttpRequest(const QString& path,
return false;
}
bool CommunicationManager::connectWebSocket()
bool CommunicationManager::connectWebSocket(const QString& sessionId)
{
if (!m_websocketChannel) {
qWarning() << "WebSocket channel not initialized";
return false;
}
if (m_websocketChannel->isConnected()) {
if(sessionId.isEmpty()){
for(auto& pChannel:m_websocketChannelMap){
if (!pChannel->isConnected()) {
pChannel->connect();
}
}
return true;
}
else
{
for(auto& pChannel:m_websocketChannelMap){
if(pChannel->sessionId() == sessionId){
if (pChannel->isConnected()) {
return true;
}
return m_websocketChannel->connect();
}
bool CommunicationManager::disconnectWebSocket()
{
if (!m_websocketChannel) {
return false;
return pChannel->connect();
}
}
}
if (!m_websocketChannel->isConnected()) {
qWarning() << "WebSocket channel not initialized";
return false;
}
bool CommunicationManager::disconnectWebSocket(const QString& sessionId)
{
if(sessionId.isEmpty()){ //不指定则断开所有通道
for(auto& pChannel:m_websocketChannelMap){
if(pChannel->sessionId() == sessionId){
if (pChannel->isConnected()) {
pChannel->disconnect();
}
}
}
return true;
}
else{
for(auto& pChannel:m_websocketChannelMap){
if(pChannel->sessionId() == sessionId){
if (!pChannel->isConnected()) {
return true;
}
return m_websocketChannel->disconnect();
}
bool CommunicationManager::sendWebSocketMessage(const QByteArray& data)
{
if (!m_websocketChannel || !m_websocketChannel->isConnected()) {
qWarning() << "WebSocket channel not connected";
return false;
return pChannel->disconnect();
}
}
}
return m_websocketChannel->send(data);
return false;
}
bool CommunicationManager::sendWebSocketText(const QString& text)
bool CommunicationManager::removeChannel(const QString& sessionId)
{
if (!m_websocketChannel || !m_websocketChannel->isConnected()) {
qWarning() << "WebSocket channel not connected";
return false;
for(auto& pChannel:m_websocketChannelMap){
if(pChannel->sessionId() == sessionId){
if (pChannel->isConnected()) {
pChannel->disconnect();
}
m_websocketChannelMap.remove(sessionId);
return true;
}
}
return false;
}
bool CommunicationManager::sendWebSocketMessage(const QByteArray& data,const QString& sessionId)
{
for(auto& pChannel:m_websocketChannelMap){
if(pChannel->sessionId() == sessionId){
if(!pChannel->isConnected()){
qWarning() << "WebSocket channel not connected";
return false;
}
return pChannel->send(data);
}
}
return m_websocketChannel->sendText(text);
qWarning() << "WebSocket channel not connected";
return false;
}
bool CommunicationManager::sendWebSocketText(const QString& text,const QString& sessionId)
{
for(auto& pChannel:m_websocketChannelMap){
if(pChannel->sessionId() == sessionId){
if(!pChannel->isConnected()){
qWarning() << "WebSocket channel not connected";
return false;
}
return pChannel->sendText(text);
}
}
qWarning() << "WebSocket channel not connected";
return false;
}
bool CommunicationManager::isHttpConnected() const
@ -213,9 +309,17 @@ bool CommunicationManager::isHttpConnected() const
return m_httpChannel && m_httpChannel->isConnected();
}
bool CommunicationManager::isWebSocketConnected() const
bool CommunicationManager::isWebSocketConnected(const QString& sessionId) const
{
return m_websocketChannel && m_websocketChannel->isConnected();
for(auto& pChannel:m_websocketChannelMap){
if(pChannel->sessionId() == sessionId){
if(pChannel->isConnected()){
return true;
}
}
}
return false;
}
ChannelConfig CommunicationManager::getHttpConfig() const
@ -247,7 +351,7 @@ void CommunicationManager::updateHttpConfig(const ChannelConfig& config)
qInfo() << "HTTP config updated";
}
void CommunicationManager::updateWebSocketConfig(const ChannelConfig& config)
void CommunicationManager::updateWebSocketConfig(const ChannelConfig& config,const QString& sessionId)
{
bool reconnect = false;
@ -256,10 +360,10 @@ void CommunicationManager::updateWebSocketConfig(const ChannelConfig& config)
}
m_websocketConfig = config;
initWebSocketChannel();
initWebSocketChannel(sessionId);
if (reconnect && config.autoConnect) {
connectWebSocket();
connectWebSocket(sessionId);
}
qInfo() << "WebSocket config updated";

View File

@ -153,8 +153,8 @@ ChannelConfig ConfigManager::getDefaultHttpConfig() const
config.id = "http_channel";
config.name = "SCADA数据接口";
config.endpoint = "http://192.168.1.100:8080/api";
config.timeout = 30000;
config.endpoint = "http://192.168.46.100:10080";
config.timeout = 10000;
config.enabled = true;
config.autoConnect = true;
config.username = "";
@ -178,7 +178,7 @@ ChannelConfig ConfigManager::getDefaultWebSocketConfig() const
config.id = "websocket_channel";
config.name = "实时数据推送";
config.endpoint = "ws://192.168.1.101:8888/ws";
config.timeout = 30000;
config.timeout = 10000;
config.enabled = true;
config.autoConnect = false;
config.connected = false;

View File

@ -27,10 +27,15 @@ DataProcessor::DataProcessor(QObject* parent)
void DataProcessor::processData(const QVariant& data,int conType)
{
qDebug() << "处理数据_size:" << data.toString().size();
qDebug() << "data_size:" << data.toJsonObject().size();
// 根据数据类型处理
processJson(data.toJsonObject(),conType);
if (data.canConvert<QJsonObject>()) {
processJson(data.toJsonObject(),conType);
}
else if(data.canConvert<QJsonArray>()){
processJsonArray(data);
}
}
void DataProcessor::processJson( const QVariant& data,int conType)
@ -38,9 +43,6 @@ void DataProcessor::processJson( const QVariant& data,int conType)
QJsonObject dataObj = data.toJsonObject();
if(conType == 0){
if(dataObj.contains("client_id")){ //实时数据相关
emit httpProcessed("recommend",data);
}
else if(dataObj.contains("input")){
emit httpProcessed("subscriptions",data);
}
}
@ -49,21 +51,11 @@ void DataProcessor::processJson( const QVariant& data,int conType)
emit websocketProcessed(data);
}
}
}
if(dataObj.contains("client_id")){ //实时数据相关
/*QString clientId = dataObj.value("client_id").toString();
QJsonArray arrTarget = dataObj.value("targets").toArray();
for (const QJsonValue& value : arrTarget) {
QJsonObject obj = value.toObject();
QString targetId = obj["id"].toString();
QString sCode = obj["code"].toString();
}*/
}
else if(dataObj.contains("input")){
}
//QMutexLocker locker(&m_mutex);
//m_dataCache[dataType] = data;
void DataProcessor::processJsonArray(const QVariant& data)
{
emit httpProcessed("recommend",data);
}
QVariant DataProcessor::getProcessedData(const QString& key) const

View File

@ -5,6 +5,7 @@
#include <QNetworkReply>
#include <QAuthenticator>
#include <QDebug>
#include <QUrlQuery>
HttpChannel::HttpChannel(const ChannelConfig& config, QObject* parent)
: BaseChannel(config, parent)
@ -35,16 +36,30 @@ bool HttpChannel::send(const QByteArray& data)
return post(data);
}
bool HttpChannel::get(const QString& path)
bool HttpChannel::get(const QString& path,const QVariantMap& queryMap)
{
if (!m_config.endpoint.isValid()) {
emit errorOccurred("Invalid endpoint");
return false;
}
QUrl url = m_config.endpoint;
if (!path.isEmpty()) {
url.setPath(url.path() + "/" + path);
QUrl url;
if(path.contains("http")){ //url全部输入
url = QUrl(path);
}
else{ //url部分输入
url = m_config.endpoint;
if (!path.isEmpty()) {
url.setPath(url.path() + path);
}
if(!queryMap.isEmpty()){
QUrlQuery query;
for(auto iter = queryMap.begin();iter != queryMap.end();++iter){
query.addQueryItem(iter.key(),iter.value().toString());
};
url.setQuery(query);
}
}
QNetworkAccessManager* manager = new QNetworkAccessManager(this);
@ -57,21 +72,25 @@ bool HttpChannel::get(const QString& path)
}
});
if(url.isEmpty())
qDebug()<<"http url empty!";
QNetworkRequest request(url);
// 设置头
for (auto it = m_headers.begin(); it != m_headers.end(); ++it) {
request.setRawHeader(it.key().toUtf8(), it.value().toUtf8());
}
// for (auto it = m_headers.begin(); it != m_headers.end(); ++it) {
// request.setRawHeader(it.key().toUtf8(), it.value().toUtf8());
// }
QNetworkReply* reply = manager->get(request);
QObject::connect(reply, &QNetworkReply::finished, [this, reply, manager]() {
QObject::connect(reply, &QNetworkReply::finished, this,[this, reply, manager]() {
if (reply->error() == QNetworkReply::NoError) {
QByteArray data = reply->readAll();
emit dataReceived(data);
} else {
emit errorOccurred(reply->errorString());
qDebug()<<reply->errorString();
}
reply->deleteLater();
@ -90,7 +109,7 @@ bool HttpChannel::post(const QByteArray& data, const QString& path)
QUrl url = m_config.endpoint;
if (!path.isEmpty()) {
url.setPath(url.path() + "/" + path);
url.setPath(url.path() + path);
}
QNetworkAccessManager* manager = new QNetworkAccessManager(this);

View File

@ -39,10 +39,9 @@ UiCommunicationBus::UiCommunicationBus(QObject* parent)
DataProcessor* processor = DataProcessor::instance();
connect(processor, &DataProcessor::httpProcessed,
this, [this](const QString& dataType, const QVariant& data) {
if(dataType == "recommend"){
// 推荐列表请求将数据分发回订阅ui
broadcastToUis(dataType, data);
}
// if(dataType == "recommend"){
// broadcastToUis(dataType, data);
// }
emit httpDataProcessed(dataType,data);
});
@ -54,12 +53,12 @@ UiCommunicationBus::UiCommunicationBus(QObject* parent)
qDebug() << "UiCommunicationBus initialized";
}
void UiCommunicationBus::sendHttpRequest(const QString& endpoint, const QVariant& data)
void UiCommunicationBus::sendHttpRequest(const QString& endpoint, const QVariant& data,const QString& method,const QVariantMap& query)
{
CommunicationManager* comm = CommunicationManager::instance();
QJsonDocument doc = QJsonDocument::fromVariant(data);
bool success = comm->sendHttpRequest(endpoint, doc.toJson());
bool success = comm->sendHttpRequest(endpoint, doc.toJson(),method,query);
if (success) {
qDebug() << "HTTP请求已发送:" << endpoint;
@ -102,7 +101,7 @@ void UiCommunicationBus::onHttpDataReceived(const QByteArray& data)
}
}
qDebug() << "HTTP响应已处理";
qDebug() << "HTTP ";
}
void UiCommunicationBus::onWebSocketDataReceived(const QByteArray& data)

View File

@ -2,7 +2,7 @@
#include "webSocketChannel.h"
#include <QDebug>
WebSocketChannel::WebSocketChannel(const ChannelConfig& config, QObject* parent)
WebSocketChannel::WebSocketChannel(const ChannelConfig& config, QString sessionId, QObject* parent)
: BaseChannel(config, parent)
, m_webSocket(new QWebSocket)
{
@ -14,6 +14,7 @@ WebSocketChannel::WebSocketChannel(const ChannelConfig& config, QObject* parent)
this, &WebSocketChannel::onTextMessageReceived);
QObject::connect(m_webSocket, &QWebSocket::binaryMessageReceived,
this, &WebSocketChannel::onBinaryMessageReceived);
m_sessionId = sessionId;
}
bool WebSocketChannel::connect()
@ -27,7 +28,24 @@ bool WebSocketChannel::connect()
return false;
}
m_webSocket->open(m_config.endpoint);
// 1. 从 WebSocket URL 提取 host 和 port
QUrl url(m_config.endpoint);
QString host = url.host();
int port = url.port(80); // 默认端口 80
// 2. 构建 Origin URL
QString origin = QString("http://%1:%2").arg(host).arg(port);
// 3. 创建 QNetworkRequest 并设置 Origin
QNetworkRequest request(url);
request.setRawHeader("Origin", origin.toUtf8());
// 4. 使用 QWebSocket 连接
QWebSocket *socket = new QWebSocket();
socket->open(request);
qDebug() << "WebSocket URL:" << m_config.endpoint;
qDebug() << "Origin:" << origin;
return true;
}
@ -73,21 +91,21 @@ void WebSocketChannel::setWebSocketConfig(const WebSocketConfig& config)
void WebSocketChannel::onConnected()
{
emit connected();
emit connected(m_sessionId);
}
void WebSocketChannel::onDisconnected()
{
emit disconnected();
emit disconnected(m_sessionId);
}
void WebSocketChannel::onTextMessageReceived(const QString& message)
{
emit textMessageReceived(message);
emit dataReceived(message.toUtf8());
emit textMessageReceived(message,m_sessionId);
emit dataReceived(message.toUtf8(),m_sessionId);
}
void WebSocketChannel::onBinaryMessageReceived(const QByteArray& message)
{
emit dataReceived(message);
emit dataReceived(message,m_sessionId);
}

View File

@ -51,7 +51,7 @@ void MonitorPagesDlg::onItemClicked(const QModelIndex &index)
{
QStandardItem* item = _pModel->itemFromIndex(index);
int nLevel = getLevel(item);
if(nLevel == 0) //顶层不响应
if(nLevel == 0 || nLevel == -1) //顶层不响应
return;
QStandardItem* parent = item->parent();
if(item)