2026-09-18 16:22:20 +08:00
|
|
|
#include "rpcservicewidget.h"
|
|
|
|
|
|
|
|
|
|
#include "robotclient.h"
|
|
|
|
|
#include "ui_rpcservicewidget.h"
|
|
|
|
|
|
|
|
|
|
#include <QComboBox>
|
|
|
|
|
#include <QJsonArray>
|
|
|
|
|
#include <QJsonDocument>
|
|
|
|
|
#include <QJsonObject>
|
|
|
|
|
#include <QLabel>
|
|
|
|
|
#include <QLineEdit>
|
|
|
|
|
#include <QPushButton>
|
|
|
|
|
#include <QTextEdit>
|
|
|
|
|
|
|
|
|
|
#ifdef CMVR_WITH_GRPC
|
|
|
|
|
#include <google/protobuf/descriptor.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
#ifdef CMVR_WITH_GRPC
|
|
|
|
|
QJsonValue defaultFieldValue(const google::protobuf::FieldDescriptor *field, int depth);
|
|
|
|
|
|
|
|
|
|
QJsonObject messageTemplate(const google::protobuf::Descriptor *descriptor, int depth)
|
|
|
|
|
{
|
|
|
|
|
QJsonObject object;
|
|
|
|
|
if (!descriptor || depth > 5)
|
|
|
|
|
return object;
|
|
|
|
|
|
|
|
|
|
for (int index = 0; index < descriptor->field_count(); ++index) {
|
|
|
|
|
const google::protobuf::FieldDescriptor *field = descriptor->field(index);
|
|
|
|
|
if (field->containing_oneof() &&
|
|
|
|
|
field->containing_oneof()->field(0) != field) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (field->name() == "timestamp")
|
|
|
|
|
continue;
|
|
|
|
|
object.insert(QString::fromStdString(field->json_name()),
|
|
|
|
|
field->is_repeated() ? QJsonValue(QJsonArray())
|
|
|
|
|
: defaultFieldValue(field, depth + 1));
|
|
|
|
|
}
|
|
|
|
|
return object;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QJsonValue defaultFieldValue(const google::protobuf::FieldDescriptor *field, int depth)
|
|
|
|
|
{
|
|
|
|
|
using Field = google::protobuf::FieldDescriptor;
|
|
|
|
|
switch (field->cpp_type()) {
|
|
|
|
|
case Field::CPPTYPE_BOOL:
|
|
|
|
|
return false;
|
|
|
|
|
case Field::CPPTYPE_INT32:
|
|
|
|
|
case Field::CPPTYPE_UINT32:
|
|
|
|
|
case Field::CPPTYPE_FLOAT:
|
|
|
|
|
case Field::CPPTYPE_DOUBLE:
|
|
|
|
|
return 0;
|
|
|
|
|
case Field::CPPTYPE_INT64:
|
|
|
|
|
case Field::CPPTYPE_UINT64:
|
|
|
|
|
return QStringLiteral("0");
|
|
|
|
|
case Field::CPPTYPE_ENUM:
|
|
|
|
|
return QString::fromStdString(field->enum_type()->value(0)->name());
|
|
|
|
|
case Field::CPPTYPE_MESSAGE:
|
|
|
|
|
return messageTemplate(field->message_type(), depth);
|
|
|
|
|
case Field::CPPTYPE_STRING:
|
|
|
|
|
default:
|
|
|
|
|
return QString();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RpcServiceWidget::RpcServiceWidget(const QString &serviceName, RobotClient *client,
|
|
|
|
|
QWidget *parent)
|
|
|
|
|
: QWidget(parent), m_ui(new Ui::RpcServiceWidget), m_client(client),
|
|
|
|
|
m_serviceName(serviceName)
|
|
|
|
|
{
|
|
|
|
|
m_ui->setupUi(this);
|
|
|
|
|
const QString shortName = serviceName.section(QLatin1Char('.'), -1);
|
|
|
|
|
m_ui->serviceNameLabel->setText(shortName);
|
|
|
|
|
m_ui->servicePathLabel->setText(serviceName);
|
|
|
|
|
m_ui->requestEdit->setFontFamily(QStringLiteral("Consolas"));
|
|
|
|
|
m_ui->responseEdit->setFontFamily(QStringLiteral("Consolas"));
|
|
|
|
|
|
|
|
|
|
#ifdef CMVR_WITH_GRPC
|
|
|
|
|
const google::protobuf::ServiceDescriptor *service =
|
|
|
|
|
google::protobuf::DescriptorPool::generated_pool()->FindServiceByName(
|
|
|
|
|
serviceName.toStdString());
|
|
|
|
|
if (service) {
|
|
|
|
|
for (int index = 0; index < service->method_count(); ++index) {
|
|
|
|
|
const google::protobuf::MethodDescriptor *method = service->method(index);
|
|
|
|
|
QString kind = QStringLiteral("Unary");
|
|
|
|
|
if (method->client_streaming() && method->server_streaming())
|
|
|
|
|
kind = QStringLiteral("Bidi stream");
|
|
|
|
|
else if (method->client_streaming())
|
|
|
|
|
kind = QStringLiteral("Client stream");
|
|
|
|
|
else if (method->server_streaming())
|
|
|
|
|
kind = QStringLiteral("Server stream");
|
|
|
|
|
m_ui->methodCombo->addItem(
|
|
|
|
|
QStringLiteral("%1 [%2]").arg(QString::fromStdString(method->name()), kind),
|
|
|
|
|
QString::fromStdString(method->name()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (m_ui->methodCombo->count() == 0) {
|
|
|
|
|
m_ui->methodCombo->addItem(QStringLiteral("没有可用的 protobuf 描述符"));
|
|
|
|
|
}
|
|
|
|
|
m_ui->executeButton->setEnabled(false);
|
|
|
|
|
|
|
|
|
|
connect(m_ui->methodCombo, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
|
|
|
|
this, &RpcServiceWidget::updateMethod);
|
|
|
|
|
connect(m_ui->resetButton, &QPushButton::clicked,
|
|
|
|
|
this, &RpcServiceWidget::resetRequest);
|
|
|
|
|
connect(m_ui->executeButton, &QPushButton::clicked,
|
|
|
|
|
this, &RpcServiceWidget::executeRequest);
|
|
|
|
|
connect(m_client, &RobotClient::rpcFinished,
|
|
|
|
|
this, &RpcServiceWidget::handleResult);
|
|
|
|
|
connect(m_client, &RobotClient::connectionChanged, this,
|
|
|
|
|
[this](bool connected, int, const QString &) {
|
|
|
|
|
m_ui->executeButton->setEnabled(connected && m_ui->methodCombo->count() > 0);
|
|
|
|
|
});
|
|
|
|
|
updateMethod();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RpcServiceWidget::~RpcServiceWidget()
|
|
|
|
|
{
|
|
|
|
|
delete m_ui;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-23 11:11:49 +08:00
|
|
|
void RpcServiceWidget::setDeviceId(const QString &deviceId)
|
|
|
|
|
{
|
|
|
|
|
m_ui->deviceIdEdit->setText(deviceId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString RpcServiceWidget::serviceName() const
|
|
|
|
|
{
|
|
|
|
|
return m_serviceName;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-18 16:22:20 +08:00
|
|
|
QString RpcServiceWidget::requestTemplate(const QString &methodName) const
|
|
|
|
|
{
|
|
|
|
|
#ifdef CMVR_WITH_GRPC
|
|
|
|
|
const google::protobuf::ServiceDescriptor *service =
|
|
|
|
|
google::protobuf::DescriptorPool::generated_pool()->FindServiceByName(
|
|
|
|
|
m_serviceName.toStdString());
|
|
|
|
|
const google::protobuf::MethodDescriptor *method =
|
|
|
|
|
service ? service->FindMethodByName(methodName.toStdString()) : nullptr;
|
|
|
|
|
if (method) {
|
|
|
|
|
const QJsonObject object = messageTemplate(method->input_type(), 0);
|
|
|
|
|
return QString::fromUtf8(QJsonDocument(object).toJson(QJsonDocument::Indented));
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
Q_UNUSED(methodName)
|
|
|
|
|
#endif
|
|
|
|
|
return QStringLiteral("{}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RpcServiceWidget::updateMethod()
|
|
|
|
|
{
|
|
|
|
|
const QString methodName = m_ui->methodCombo->currentData().toString();
|
|
|
|
|
if (methodName.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
#ifdef CMVR_WITH_GRPC
|
|
|
|
|
const google::protobuf::ServiceDescriptor *service =
|
|
|
|
|
google::protobuf::DescriptorPool::generated_pool()->FindServiceByName(
|
|
|
|
|
m_serviceName.toStdString());
|
|
|
|
|
const google::protobuf::MethodDescriptor *method =
|
|
|
|
|
service ? service->FindMethodByName(methodName.toStdString()) : nullptr;
|
|
|
|
|
if (method) {
|
|
|
|
|
QString mode = QStringLiteral("Unary RPC");
|
|
|
|
|
if (method->client_streaming() && method->server_streaming())
|
|
|
|
|
mode = QStringLiteral("双向流:请求可填写 JSON 数组,最多接收 100 条反馈");
|
|
|
|
|
else if (method->client_streaming())
|
|
|
|
|
mode = QStringLiteral("客户端流:请求填写 JSON 数组");
|
|
|
|
|
else if (method->server_streaming())
|
|
|
|
|
mode = QStringLiteral("服务端流:执行后接收最多 5 秒或 100 条反馈");
|
|
|
|
|
m_ui->methodDetailLabel->setText(
|
|
|
|
|
QStringLiteral("%1 -> %2 %3")
|
|
|
|
|
.arg(QString::fromStdString(method->input_type()->full_name()),
|
|
|
|
|
QString::fromStdString(method->output_type()->full_name()), mode));
|
|
|
|
|
m_ui->executeButton->setText(method->client_streaming() || method->server_streaming()
|
|
|
|
|
? QStringLiteral("执行流式 RPC")
|
|
|
|
|
: QStringLiteral("执行 RPC"));
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
resetRequest();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RpcServiceWidget::resetRequest()
|
|
|
|
|
{
|
|
|
|
|
const QString methodName = m_ui->methodCombo->currentData().toString();
|
|
|
|
|
m_ui->requestEdit->setPlainText(requestTemplate(methodName));
|
|
|
|
|
m_ui->responseEdit->clear();
|
|
|
|
|
m_ui->resultLabel->setText(QStringLiteral("等待执行"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RpcServiceWidget::executeRequest()
|
|
|
|
|
{
|
|
|
|
|
const QString methodName = m_ui->methodCombo->currentData().toString();
|
|
|
|
|
if (methodName.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
m_pendingMethod = methodName;
|
|
|
|
|
m_ui->executeButton->setEnabled(false);
|
|
|
|
|
m_ui->resultLabel->setText(QStringLiteral("正在调用..."));
|
|
|
|
|
m_ui->responseEdit->clear();
|
|
|
|
|
m_client->requestRpc(m_serviceName, methodName,
|
|
|
|
|
m_ui->requestEdit->toPlainText(),
|
|
|
|
|
m_ui->deviceIdEdit->text().trimmed());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RpcServiceWidget::handleResult(const QString &serviceName, const QString &methodName,
|
|
|
|
|
bool success, const QString &responseJson)
|
|
|
|
|
{
|
|
|
|
|
if (serviceName != m_serviceName || methodName != m_pendingMethod)
|
|
|
|
|
return;
|
|
|
|
|
m_pendingMethod.clear();
|
|
|
|
|
m_ui->executeButton->setEnabled(true);
|
|
|
|
|
m_ui->resultLabel->setText(success ? QStringLiteral("调用成功")
|
|
|
|
|
: QStringLiteral("调用失败"));
|
|
|
|
|
m_ui->responseEdit->setPlainText(responseJson);
|
|
|
|
|
}
|