云端数据服务对接,上传接口写完,等待他们写完接口。
This commit is contained in:
parent
adc4e3c131
commit
8b487ed28f
|
|
@ -188,3 +188,33 @@ void ApiClient::getBinary(const QString &path, BinarySuccessCB onSuccess, Failed
|
|||
})
|
||||
.exec();
|
||||
}
|
||||
|
||||
void ApiClient::postBinary(const QString &path, const QByteArray &body, SuccessCB onSuccess, FailedCB onFailed)
|
||||
{
|
||||
postBinary(path, body, QJsonObject(), onSuccess, onFailed);
|
||||
}
|
||||
|
||||
void ApiClient::postBinary(const QString &path, const QByteArray &body, const QJsonObject &headers,
|
||||
SuccessCB onSuccess, FailedCB onFailed)
|
||||
{
|
||||
qDebug() << "ApiClient::postBinary " << path << " bytes=" << body.size();
|
||||
QString url = joinUrl(m_baseUrl, path);
|
||||
|
||||
auto req = m_httpClient->post(url).body(body);
|
||||
req.header("Content-Type", "application/octet-stream");
|
||||
if (!m_token.isEmpty()) req.header("Authorization", m_token);
|
||||
|
||||
// 添加自定义请求头(metadata 信息)
|
||||
for (auto it = headers.begin(); it != headers.end(); ++it)
|
||||
{
|
||||
req.header("X-Metadata-" + it.key(), it.value().toVariant());
|
||||
}
|
||||
|
||||
req.onSuccess([this, onSuccess, onFailed](const QJsonObject &resp) {
|
||||
this->handleResponse(resp, onSuccess, onFailed);
|
||||
})
|
||||
.onFailed([onFailed](const QString &err) {
|
||||
if (onFailed) onFailed(err);
|
||||
})
|
||||
.exec();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,6 +72,13 @@ public:
|
|||
// 获取二进制原始数据(不走 JSON 解析,直接返回 QByteArray)
|
||||
void getBinary(const QString &path, BinarySuccessCB onSuccess, FailedCB onFailed);
|
||||
|
||||
// 上传二进制原始数据(body 为 QByteArray,Content-Type: application/octet-stream)
|
||||
void postBinary(const QString &path, const QByteArray &body, SuccessCB onSuccess, FailedCB onFailed);
|
||||
|
||||
// 上传二进制原始数据 + 自定义请求头(metadata 信息放在 header 里,一次发送)
|
||||
void postBinary(const QString &path, const QByteArray &body, const QJsonObject &headers,
|
||||
SuccessCB onSuccess, FailedCB onFailed);
|
||||
|
||||
private:
|
||||
void handleResponse(const QJsonObject &resp, SuccessCB onSuccess, FailedCB onFailed);
|
||||
|
||||
|
|
|
|||
|
|
@ -156,6 +156,7 @@ void CloudDataDlg::initTreeWells(QJsonObject jObj)
|
|||
// 井
|
||||
QTreeWidgetItem *itemJing = new QTreeWidgetItem();
|
||||
itemJing->setText(0, obj["wellName"].toString());
|
||||
itemJing->setData(0, Qt::UserRole, "WELL"); // 类型
|
||||
itemJing->setData(0, Qt::UserRole + 1, obj["wellId"].toString()); // 存储额外数据,项目名
|
||||
//
|
||||
QIcon icon;
|
||||
|
|
@ -177,6 +178,7 @@ void CloudDataDlg::initTreeWells(QJsonObject jObj)
|
|||
|
||||
QTreeWidgetItem *itemJingCi = new QTreeWidgetItem();
|
||||
itemJingCi->setText(0, staObj["stageName"].toString());
|
||||
itemJingCi->setData(0, Qt::UserRole, "STAGE"); // 类型
|
||||
itemJingCi->setData(0, Qt::UserRole + 1, staObj["stageId"].toString()); // 存储额外数据,项目名
|
||||
//
|
||||
QIcon iconci;
|
||||
|
|
@ -245,9 +247,11 @@ bool CloudDataDlg::initTreeData(QTreeWidgetItem* itemJingCi, QJsonObject& jObjCi
|
|||
itemCurveLog->setText(0, sName);
|
||||
itemCurveLog->setData(0, Qt::UserRole, sTypeObj); // 存储额外数据,如ID
|
||||
itemCurveLog->setData(0, Qt::UserRole + 1, tmpItem["metadataId"].toString()); // 存储额外数据,井次文件路径
|
||||
itemCurveLog->setData(0, Qt::UserRole + 2, itemJingCi->data(0, Qt::UserRole + 1)); // 井次id
|
||||
itemCurveLog->setData(0, Qt::UserRole + 3, itemJingCi->parent()->data(0, Qt::UserRole + 1)); // 井id
|
||||
//
|
||||
QIcon iconLog;
|
||||
iconLog.addPixmap(QPixmap(GetImagePath() + strIcon1), QIcon::Selected);
|
||||
iconLog.addPixmap(QPixmap(GetImagePath() + strIcon2), QIcon::Selected);
|
||||
iconLog.addPixmap(QPixmap(GetImagePath() + strIcon2), QIcon::Normal);
|
||||
itemCurveLog->setIcon(0, iconLog);
|
||||
itemCurve->addChild(itemCurveLog);
|
||||
|
|
@ -275,6 +279,7 @@ void CloudDataDlg::getList()
|
|||
|
||||
void CloudDataDlg::getWells(QString projectId)
|
||||
{
|
||||
m_strProjectId = projectId;
|
||||
QString sapi = "/core/project/tree?projectId=" + projectId;
|
||||
ApiClient::getInstance()->get(sapi,
|
||||
[this](const QJsonObject& response)
|
||||
|
|
@ -394,6 +399,282 @@ void CloudDataDlg::on_btn_import_clicked()
|
|||
);
|
||||
}
|
||||
|
||||
void CloudDataDlg::on_btn_export_clicked()
|
||||
{
|
||||
// 1. 获取云端目标位置(wellId, stageId)
|
||||
QString strWellId = "";
|
||||
QString strStageId = "";
|
||||
foreach(QTreeWidgetItem *pItem, ui->treeWidget->selectedItems())
|
||||
{
|
||||
getWellStageId(pItem, strWellId, strStageId);
|
||||
if (strWellId.length() > 0 && strStageId.length() > 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (strWellId.isEmpty() || strStageId.isEmpty())
|
||||
{
|
||||
QMessageBox::warning(this, "提示", "请先在树图中选择导出到的目标井次!");
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 获取本地 SLF 文件路径和选中的曲线名
|
||||
if (!m_projectWidgets)
|
||||
return;
|
||||
QString strSlfName = "";
|
||||
QString strWellName = "";
|
||||
QString strLeft = m_projectWidgets->getLeftTreeString();
|
||||
if (strLeft.length() > 0)
|
||||
{
|
||||
QStringList list = strLeft.split("#@@#");
|
||||
if (list.size() > 3)
|
||||
{
|
||||
strSlfName = list[0];
|
||||
strWellName = list[1];
|
||||
}
|
||||
}
|
||||
if (strSlfName.isEmpty())
|
||||
{
|
||||
QMessageBox::warning(this, "提示", "请先在左侧树图中选中要导出的井次!");
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. 从 m_mapShowObject 获取选中的对象名
|
||||
QStringList curveNames;
|
||||
if (m_projectWidgets->m_mapShowObject.contains(strSlfName))
|
||||
{
|
||||
curveNames = m_projectWidgets->m_mapShowObject[strSlfName];
|
||||
}
|
||||
if (curveNames.isEmpty())
|
||||
{
|
||||
QMessageBox::warning(this, "提示", "请先在左侧树图中选中要导出的曲线/波列!");
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << "导出到云端目标位置:" << strWellId << strStageId
|
||||
<< "SLF:" << strSlfName << "曲线:" << curveNames;
|
||||
|
||||
// 4. 从 SLF 文件读取数据
|
||||
QVector<CloudExportItem> items = readLocalData(strSlfName, curveNames);
|
||||
if (items.isEmpty())
|
||||
{
|
||||
QMessageBox::warning(this, "提示", "未从SLF文件中读取到有效数据!");
|
||||
return;
|
||||
}
|
||||
|
||||
// 5. 禁用按钮,防止重复点击
|
||||
ui->btn_export->setEnabled(false);
|
||||
ui->btn_export->setText("导出中...");
|
||||
|
||||
// 6. 批量异步上传
|
||||
exportCloudData(m_strProjectId, strWellId, strStageId, items,
|
||||
[this](const QVector<CloudExportItem>& results)
|
||||
{
|
||||
this->ui->btn_export->setEnabled(true);
|
||||
this->ui->btn_export->setText("导出");
|
||||
|
||||
int successCount = 0;
|
||||
int failCount = 0;
|
||||
for (const auto& r : results)
|
||||
{
|
||||
if (r.metadataId.isEmpty())
|
||||
failCount++;
|
||||
else
|
||||
successCount++;
|
||||
}
|
||||
|
||||
qDebug() << "===== 云端导出完成 =====";
|
||||
qDebug() << " 成功:" << successCount << "失败:" << failCount;
|
||||
|
||||
QString msg = QString("导出完成!成功 %1 条,失败 %2 条").arg(successCount).arg(failCount);
|
||||
QMessageBox::information(this, "提示", msg);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// 从本地 SLF 文件读取曲线/波列数据
|
||||
QVector<CloudExportItem> CloudDataDlg::readLocalData(const QString& strSlfName, const QStringList& curveNames)
|
||||
{
|
||||
QVector<CloudExportItem> items;
|
||||
|
||||
CMemRdWt logio;
|
||||
if (!logio.Open(strSlfName.toStdString().c_str(), CMemRdWt::modeRead))
|
||||
{
|
||||
qDebug() << "readLocalData: SLF文件打开失败:" << strSlfName;
|
||||
return items;
|
||||
}
|
||||
|
||||
for (const QString& curveName : curveNames)
|
||||
{
|
||||
QByteArray nameBytes = curveName.toLocal8Bit();
|
||||
|
||||
// 先尝试作为曲线打开
|
||||
int nCurveIdx = logio.OpenCurve(nameBytes.data());
|
||||
if (nCurveIdx >= 0)
|
||||
{
|
||||
Slf_CURVE curveInfo;
|
||||
logio.GetCurveInfo(nCurveIdx, &curveInfo);
|
||||
|
||||
DWORD sampleCount = (DWORD)((curveInfo.EndDepth - curveInfo.StartDepth) / curveInfo.DepLevel + 1.5);
|
||||
float* buffer = new float[sampleCount];
|
||||
DWORD actual = logio.ReadCurveToFloatBuf(nCurveIdx, curveInfo.StartDepth, sampleCount, buffer);
|
||||
|
||||
CloudExportItem item;
|
||||
item.curveName = curveName;
|
||||
item.dataType = "curveObject";
|
||||
item.startDepth = curveInfo.StartDepth;
|
||||
item.endDepth = curveInfo.EndDepth;
|
||||
item.depLevel = curveInfo.DepLevel;
|
||||
item.unit = QString::fromLocal8Bit(curveInfo.Unit);
|
||||
item.data.resize(actual);
|
||||
memcpy(item.data.data(), buffer, actual * sizeof(float));
|
||||
|
||||
delete[] buffer;
|
||||
logio.CloseCurve(nCurveIdx);
|
||||
|
||||
items.append(item);
|
||||
qDebug() << "读取曲线:" << curveName << "深度:" << item.startDepth << "-" << item.endDepth
|
||||
<< "采样数:" << actual;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 再尝试作为波列打开
|
||||
int nWaveIdx = logio.OpenWave(nameBytes.data());
|
||||
if (nWaveIdx >= 0)
|
||||
{
|
||||
Slf_WAVE waveInfo;
|
||||
logio.GetWaveInfo(nWaveIdx, &waveInfo);
|
||||
|
||||
DWORD sampleCount = (DWORD)((waveInfo.EndDepth - waveInfo.StartDepth) / waveInfo.DepLevel + 1.5);
|
||||
// 波列数据量 = 采样点数 × 时间采样数 × 阵列数
|
||||
DWORD totalFloats = sampleCount * waveInfo.TimeSamples * waveInfo.ArrayNum;
|
||||
float* buffer = new float[totalFloats];
|
||||
DWORD actual = logio.ReadWaveToFloatBuf(nWaveIdx, waveInfo.StartDepth, sampleCount, buffer);
|
||||
|
||||
CloudExportItem item;
|
||||
item.curveName = curveName;
|
||||
item.dataType = "waveObject";
|
||||
item.startDepth = waveInfo.StartDepth;
|
||||
item.endDepth = waveInfo.EndDepth;
|
||||
item.depLevel = waveInfo.DepLevel;
|
||||
item.unit = QString::fromLocal8Bit(waveInfo.Unit);
|
||||
item.data.resize(actual);
|
||||
memcpy(item.data.data(), buffer, actual * sizeof(float));
|
||||
|
||||
delete[] buffer;
|
||||
logio.CloseWave(nWaveIdx);
|
||||
|
||||
items.append(item);
|
||||
qDebug() << "读取波列:" << curveName << "深度:" << item.startDepth << "-" << item.endDepth
|
||||
<< "采样数:" << actual;
|
||||
continue;
|
||||
}
|
||||
|
||||
qDebug() << "未找到对象:" << curveName;
|
||||
}
|
||||
|
||||
logio.Close();
|
||||
return items;
|
||||
}
|
||||
|
||||
// 异步上传单个数据项到云端(metadata 放请求头,一次发送)
|
||||
void CloudDataDlg::uploadCloudData(const QString& proId, const QString& wellId, const QString& stageId,
|
||||
const CloudExportItem& item,
|
||||
std::function<void(const CloudExportItem&)> onSuccess,
|
||||
std::function<void(const QString&)> onFailed)
|
||||
{
|
||||
// 构建 metadata 请求头
|
||||
QJsonObject headers;
|
||||
headers["ProjectId"] = proId; // 项目id
|
||||
headers["WellId"] = wellId; // 井id
|
||||
headers["StageId"] = stageId; // 井次id
|
||||
headers["DataType"] = item.dataType; // 类型 曲线or波列
|
||||
headers["CurveName"] = item.curveName; // 名称
|
||||
headers["StartDepth"] = (double)item.startDepth; // 顶深
|
||||
headers["EndDepth"] = (double)item.endDepth; // 底深
|
||||
headers["DepLevel"] = (double)item.depLevel; // 采样间隔
|
||||
headers["Unit"] = item.unit; // 单位
|
||||
|
||||
// 二进制数据
|
||||
QByteArray binaryData;
|
||||
binaryData.resize(item.data.size() * sizeof(float));
|
||||
memcpy(binaryData.data(), item.data.constData(), item.data.size() * sizeof(float));
|
||||
|
||||
// 根据数据类型确定上传路径
|
||||
QString dataPath = (item.dataType == "waveObject")
|
||||
? "/core/array-data/upload"
|
||||
: "/core/curve-data/upload";
|
||||
|
||||
ApiClient::getInstance()->postBinary(dataPath, binaryData, headers,
|
||||
[item, onSuccess](const QJsonObject& resp)
|
||||
{
|
||||
qDebug() << "上传数据成功:" << item.curveName;
|
||||
CloudExportItem result = item;
|
||||
result.metadataId = resp.value("metadataId").toString(resp.value("id").toString());
|
||||
if (onSuccess) onSuccess(result);
|
||||
},
|
||||
[item, onFailed](const QString& error)
|
||||
{
|
||||
qDebug() << "上传数据失败:" << item.curveName << error;
|
||||
if (onFailed) onFailed(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// 批量异步导出
|
||||
void CloudDataDlg::exportCloudData(const QString& proId, const QString& wellId, const QString& stageId,
|
||||
const QVector<CloudExportItem>& items,
|
||||
std::function<void(const QVector<CloudExportItem>&)> onAllDone)
|
||||
{
|
||||
if (items.isEmpty())
|
||||
{
|
||||
if (onAllDone) onAllDone(QVector<CloudExportItem>());
|
||||
return;
|
||||
}
|
||||
|
||||
int totalCount = items.size();
|
||||
QVector<CloudExportItem>* results = new QVector<CloudExportItem>();
|
||||
int* failCount = new int(0);
|
||||
|
||||
for (int i = 0; i < items.size(); i++)
|
||||
{
|
||||
const CloudExportItem& item = items[i];
|
||||
|
||||
uploadCloudData(proId, wellId, stageId, item,
|
||||
[results, failCount, totalCount, onAllDone](const CloudExportItem& result)
|
||||
{
|
||||
results->append(result);
|
||||
|
||||
if (results->size() + (*failCount) >= totalCount)
|
||||
{
|
||||
QVector<CloudExportItem> finalResults = *results;
|
||||
delete results;
|
||||
delete failCount;
|
||||
if (onAllDone) onAllDone(finalResults);
|
||||
}
|
||||
},
|
||||
[results, failCount, totalCount, onAllDone, item](const QString& error)
|
||||
{
|
||||
(*failCount)++;
|
||||
// 失败的也加入结果(metadataId 为空表示失败)
|
||||
CloudExportItem failed = item;
|
||||
failed.metadataId = "";
|
||||
results->append(failed);
|
||||
|
||||
qDebug() << "导出失败:" << item.curveName << error;
|
||||
|
||||
if (results->size() + (*failCount) >= totalCount)
|
||||
{
|
||||
QVector<CloudExportItem> finalResults = *results;
|
||||
delete results;
|
||||
delete failCount;
|
||||
if (onAllDone) onAllDone(finalResults);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 异步获取单个 metadataId 的数据
|
||||
void CloudDataDlg::fetchCloudData(const QString& metadataId, const QString& dataType, const QString& dataName,
|
||||
std::function<void(const CloudImportResult&)> onSuccess,
|
||||
|
|
@ -562,3 +843,25 @@ void CloudDataDlg::paintEvent(QPaintEvent *pevt)
|
|||
{
|
||||
QDialog::paintEvent(pevt);
|
||||
}
|
||||
|
||||
void CloudDataDlg::getWellStageId(QTreeWidgetItem* pItem, QString& strWellId, QString& strStageId)
|
||||
{
|
||||
QString strtype = pItem->data(0, Qt::UserRole).toString();
|
||||
if ("WELL" == strtype)
|
||||
{
|
||||
strWellId = pItem->data(0, Qt::UserRole + 1).toString();
|
||||
}
|
||||
else if ("STAGE" == strtype)
|
||||
{
|
||||
strStageId = pItem->data(0, Qt::UserRole + 1).toString();
|
||||
}
|
||||
|
||||
if (strWellId == "" || strStageId == "")
|
||||
{
|
||||
QTreeWidgetItem *parentItem = pItem->parent(); // 上两层目录是井次
|
||||
if (parentItem)
|
||||
{
|
||||
getWellStageId(parentItem, strWellId, strStageId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,19 @@ struct CloudImportResult
|
|||
QVector<float> data; // float二进制数组
|
||||
};
|
||||
|
||||
// 云端导出数据项(从本地SLF读取的曲线/波列数据)
|
||||
struct CloudExportItem
|
||||
{
|
||||
QString curveName; // 曲线/波列名
|
||||
QString dataType; // curveObject / waveObject
|
||||
float startDepth; // 顶深
|
||||
float endDepth; // 底深
|
||||
float depLevel; // 采样间隔
|
||||
QString unit; // 单位
|
||||
QString metadataId; // 上传后服务端返回的ID
|
||||
QVector<float> data; // float二进制数组
|
||||
};
|
||||
|
||||
// 云端导入对话框
|
||||
class CloudDataDlg : public QDialog
|
||||
{
|
||||
|
|
@ -46,12 +59,30 @@ public:
|
|||
void importCloudData(const QVector<QString>& vecId, const QMap<QString, QVector<QString>>& mapSelect,
|
||||
std::function<void(const QVector<CloudImportResult>&)> onAllDone = nullptr);
|
||||
|
||||
// ===== 云端数据导出接口 =====
|
||||
// 从本地 SLF 文件读取曲线/波列数据
|
||||
QVector<CloudExportItem> readLocalData(const QString& strSlfName, const QStringList& curveNames);
|
||||
|
||||
// 异步上传单个数据项到云端(先创建metadata,再上传二进制数据)
|
||||
void uploadCloudData(const QString& proId, const QString& wellId, const QString& stageId, const CloudExportItem& item,
|
||||
std::function<void(const CloudExportItem&)> onSuccess,
|
||||
std::function<void(const QString&)> onFailed);
|
||||
|
||||
// 批量异步导出,所有上传完成后回调
|
||||
void exportCloudData(const QString& proId, const QString& wellId, const QString& stageId,
|
||||
const QVector<CloudExportItem>& items,
|
||||
std::function<void(const QVector<CloudExportItem>&)> onAllDone = nullptr);
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent *pevt);
|
||||
|
||||
void getWellStageId(QTreeWidgetItem* pItem, QString& strWellId, QString& strStageId);
|
||||
|
||||
private slots:
|
||||
void on_btn_back_clicked();
|
||||
void on_btn_import_clicked();
|
||||
void on_btn_export_clicked();
|
||||
|
||||
|
||||
void onItemDoubleClicked(QTreeWidgetItem* item, int index);//鼠标双击tree菜单项
|
||||
|
||||
|
|
@ -63,7 +94,9 @@ private:
|
|||
|
||||
QtProjectWidgets *m_projectWidgets = NULL;
|
||||
|
||||
|
||||
QString m_strProjectId = "";
|
||||
QString m_strWellId = "";
|
||||
QString m_strStageId = "";
|
||||
};
|
||||
|
||||
#endif // CloudDataDlg_H
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>边框线形设置</string>
|
||||
<string>云端数据服务</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="1,0">
|
||||
<item>
|
||||
|
|
@ -163,6 +163,22 @@
|
|||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_export">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>数据导入到云端</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="widget_2" native="true"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_import">
|
||||
<property name="minimumSize">
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user