|
|
|
@@ -253,6 +253,28 @@ bool parseAddress( |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
QJsonObject serializeDataPoint(const DataPoint &data_point) |
|
|
|
{ |
|
|
|
QJsonObject object; |
|
|
|
object.insert(QStringLiteral("address"), serializeAddress(data_point.address)); |
|
|
|
object.insert(QStringLiteral("name"), fromUtf8(data_point.name)); |
|
|
|
object.insert(QStringLiteral("comment"), fromUtf8(data_point.comment)); |
|
|
|
return object; |
|
|
|
} |
|
|
|
|
|
|
|
bool parseDataPoint( |
|
|
|
const QJsonObject &object, |
|
|
|
const std::string &context, |
|
|
|
DataPoint *data_point, |
|
|
|
ParseState *state) |
|
|
|
{ |
|
|
|
QJsonObject address; |
|
|
|
return readObject(object, "address", context, &address, state) |
|
|
|
&& parseAddress(address, context + ".address", &data_point->address, state) |
|
|
|
&& readString(object, "name", context, &data_point->name, state) |
|
|
|
&& readString(object, "comment", context, &data_point->comment, state); |
|
|
|
} |
|
|
|
|
|
|
|
// 将 HMI 控件类型枚举转换为工程文件中的稳定字符串 |
|
|
|
QString hmiControlTypeName(HmiControlType type) |
|
|
|
{ |
|
|
|
@@ -816,80 +838,124 @@ bool parseLogicNode( |
|
|
|
{ |
|
|
|
QJsonObject config; |
|
|
|
if (!readString(object, "id", context, &node->id, state) |
|
|
|
|| !readBool(object, "configured", context, &node->configured, state) |
|
|
|
|| !readObject(object, "config", context, &config, state) |
|
|
|
|| !parseNodeConfig(config, context + ".config", &node->config, state)) |
|
|
|
{ |
|
|
|
return false; |
|
|
|
} |
|
|
|
// 1.0 旧工程没有 configured 字段,按历史默认地址视为已配置 |
|
|
|
node->configured = true; |
|
|
|
if (object.contains(QStringLiteral("configured")) |
|
|
|
&& !readBool(object, "configured", context, &node->configured, state)) |
|
|
|
{ |
|
|
|
return false; |
|
|
|
} |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
QJsonObject serializeLadderStage(const LadderStage &stage) |
|
|
|
QString expressionKindText(ConditionExpressionKind kind) |
|
|
|
{ |
|
|
|
QJsonArray branches; |
|
|
|
for (const LogicNode &node : stage.branches) |
|
|
|
switch (kind) |
|
|
|
{ |
|
|
|
branches.append(serializeLogicNode(node)); |
|
|
|
case ConditionExpressionKind::Node: |
|
|
|
return QStringLiteral("node"); |
|
|
|
case ConditionExpressionKind::Series: |
|
|
|
return QStringLiteral("series"); |
|
|
|
case ConditionExpressionKind::Parallel: |
|
|
|
return QStringLiteral("parallel"); |
|
|
|
} |
|
|
|
return {}; |
|
|
|
} |
|
|
|
|
|
|
|
QJsonObject serializeConditionExpression(const ConditionExpression &expression) |
|
|
|
{ |
|
|
|
QJsonObject object; |
|
|
|
object.insert(QStringLiteral("id"), fromUtf8(stage.id)); |
|
|
|
object.insert(QStringLiteral("branches"), branches); |
|
|
|
object.insert(QStringLiteral("id"), fromUtf8(expression.id)); |
|
|
|
object.insert(QStringLiteral("kind"), expressionKindText(expression.kind)); |
|
|
|
if (expression.kind == ConditionExpressionKind::Node) |
|
|
|
{ |
|
|
|
object.insert(QStringLiteral("node"), serializeLogicNode(*expression.node)); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
QJsonArray children; |
|
|
|
for (const ConditionExpression &child : expression.children) |
|
|
|
{ |
|
|
|
children.append(serializeConditionExpression(child)); |
|
|
|
} |
|
|
|
object.insert(QStringLiteral("children"), children); |
|
|
|
} |
|
|
|
return object; |
|
|
|
} |
|
|
|
|
|
|
|
bool parseLadderStage( |
|
|
|
bool parseConditionExpression( |
|
|
|
const QJsonObject &object, |
|
|
|
const std::string &context, |
|
|
|
LadderStage *stage, |
|
|
|
ConditionExpression *expression, |
|
|
|
ParseState *state) |
|
|
|
{ |
|
|
|
QJsonArray branches; |
|
|
|
if (!readString(object, "id", context, &stage->id, state) |
|
|
|
|| !readArray(object, "branches", context, &branches, state)) |
|
|
|
std::string kind; |
|
|
|
if (!readString(object, "id", context, &expression->id, state) |
|
|
|
|| !readString(object, "kind", context, &kind, state)) |
|
|
|
{ |
|
|
|
return false; |
|
|
|
} |
|
|
|
stage->branches.reserve(static_cast<std::size_t>(branches.size())); |
|
|
|
for (int index = 0; index < branches.size(); ++index) |
|
|
|
if (kind == "node") |
|
|
|
{ |
|
|
|
if (!branches.at(index).isObject()) |
|
|
|
QJsonObject node; |
|
|
|
if (!readObject(object, "node", context, &node, state)) |
|
|
|
{ |
|
|
|
return false; |
|
|
|
} |
|
|
|
LogicNode parsed_node; |
|
|
|
if (!parseLogicNode(node, context + ".node", &parsed_node, state)) |
|
|
|
{ |
|
|
|
return false; |
|
|
|
} |
|
|
|
expression->kind = ConditionExpressionKind::Node; |
|
|
|
expression->node = std::move(parsed_node); |
|
|
|
return true; |
|
|
|
} |
|
|
|
if (kind != "series" && kind != "parallel") |
|
|
|
{ |
|
|
|
return state->fail( |
|
|
|
ProjectStorageError::InvalidField, |
|
|
|
context + ".kind must be node, series or parallel"); |
|
|
|
} |
|
|
|
QJsonArray children; |
|
|
|
if (!readArray(object, "children", context, &children, state)) |
|
|
|
{ |
|
|
|
return false; |
|
|
|
} |
|
|
|
expression->kind = kind == "series" |
|
|
|
? ConditionExpressionKind::Series : ConditionExpressionKind::Parallel; |
|
|
|
expression->children.reserve(static_cast<std::size_t>(children.size())); |
|
|
|
for (int index = 0; index < children.size(); ++index) |
|
|
|
{ |
|
|
|
if (!children.at(index).isObject()) |
|
|
|
{ |
|
|
|
return state->fail( |
|
|
|
ProjectStorageError::InvalidField, |
|
|
|
context + ".branches items must be objects"); |
|
|
|
context + ".children items must be objects"); |
|
|
|
} |
|
|
|
LogicNode node; |
|
|
|
if (!parseLogicNode( |
|
|
|
branches.at(index).toObject(), |
|
|
|
context + ".branches[" + std::to_string(index) + ']', |
|
|
|
&node, |
|
|
|
ConditionExpression child; |
|
|
|
if (!parseConditionExpression( |
|
|
|
children.at(index).toObject(), |
|
|
|
context + ".children[" + std::to_string(index) + ']', |
|
|
|
&child, |
|
|
|
state)) |
|
|
|
{ |
|
|
|
return false; |
|
|
|
} |
|
|
|
stage->branches.push_back(std::move(node)); |
|
|
|
expression->children.push_back(std::move(child)); |
|
|
|
} |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
QJsonObject serializeLadderRung(const LadderRung &rung) |
|
|
|
{ |
|
|
|
QJsonArray stages; |
|
|
|
for (const LadderStage &stage : rung.stages) |
|
|
|
{ |
|
|
|
stages.append(serializeLadderStage(stage)); |
|
|
|
} |
|
|
|
QJsonObject object; |
|
|
|
object.insert(QStringLiteral("id"), fromUtf8(rung.id)); |
|
|
|
object.insert(QStringLiteral("name"), fromUtf8(rung.name)); |
|
|
|
object.insert(QStringLiteral("stages"), stages); |
|
|
|
object.insert( |
|
|
|
QStringLiteral("condition"), |
|
|
|
rung.condition.has_value() |
|
|
|
? QJsonValue(serializeConditionExpression(*rung.condition)) |
|
|
|
: QJsonValue(QJsonValue::Null)); |
|
|
|
object.insert( |
|
|
|
QStringLiteral("output"), |
|
|
|
rung.output.has_value() ? QJsonValue(serializeLogicNode(*rung.output)) |
|
|
|
@@ -903,34 +969,37 @@ bool parseLadderRung( |
|
|
|
LadderRung *rung, |
|
|
|
ParseState *state) |
|
|
|
{ |
|
|
|
QJsonArray stages; |
|
|
|
QJsonValue condition; |
|
|
|
QJsonValue output; |
|
|
|
if (!readString(object, "id", context, &rung->id, state) |
|
|
|
|| !readString(object, "name", context, &rung->name, state) |
|
|
|
|| !readArray(object, "stages", context, &stages, state) |
|
|
|
|| !readValue(object, "output", context, &output, state)) |
|
|
|
{ |
|
|
|
return false; |
|
|
|
} |
|
|
|
rung->stages.reserve(static_cast<std::size_t>(stages.size())); |
|
|
|
for (int index = 0; index < stages.size(); ++index) |
|
|
|
if (!readValue(object, "condition", context, &condition, state)) |
|
|
|
{ |
|
|
|
if (!stages.at(index).isObject()) |
|
|
|
{ |
|
|
|
return state->fail( |
|
|
|
ProjectStorageError::InvalidField, |
|
|
|
context + ".stages items must be objects"); |
|
|
|
} |
|
|
|
LadderStage stage; |
|
|
|
if (!parseLadderStage( |
|
|
|
stages.at(index).toObject(), |
|
|
|
context + ".stages[" + std::to_string(index) + ']', |
|
|
|
&stage, |
|
|
|
state)) |
|
|
|
return false; |
|
|
|
} |
|
|
|
if (condition.isNull()) |
|
|
|
{ |
|
|
|
rung->condition.reset(); |
|
|
|
} |
|
|
|
else if (!condition.isObject()) |
|
|
|
{ |
|
|
|
return state->fail( |
|
|
|
ProjectStorageError::InvalidField, |
|
|
|
context + ".condition must be an object or null"); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
ConditionExpression parsed_condition; |
|
|
|
if (!parseConditionExpression( |
|
|
|
condition.toObject(), context + ".condition", &parsed_condition, state)) |
|
|
|
{ |
|
|
|
return false; |
|
|
|
} |
|
|
|
rung->stages.push_back(std::move(stage)); |
|
|
|
rung->condition = std::move(parsed_condition); |
|
|
|
} |
|
|
|
if (output.isNull()) |
|
|
|
{ |
|
|
|
@@ -1019,12 +1088,19 @@ QJsonObject serializeProject(const Project &project) |
|
|
|
logics.append(serializeControlLogic(logic)); |
|
|
|
} |
|
|
|
|
|
|
|
QJsonArray data_points; |
|
|
|
for (const DataPoint &data_point : project.dataPoints) |
|
|
|
{ |
|
|
|
data_points.append(serializeDataPoint(data_point)); |
|
|
|
} |
|
|
|
|
|
|
|
QJsonObject object; |
|
|
|
object.insert(QStringLiteral("formatVersion"), fromUtf8(project.metadata.formatVersion)); |
|
|
|
object.insert(QStringLiteral("id"), fromUtf8(project.metadata.id)); |
|
|
|
object.insert(QStringLiteral("name"), fromUtf8(project.metadata.name)); |
|
|
|
object.insert(QStringLiteral("hmiPages"), pages); |
|
|
|
object.insert(QStringLiteral("controlLogics"), logics); |
|
|
|
object.insert(QStringLiteral("dataPoints"), data_points); |
|
|
|
return object; |
|
|
|
} |
|
|
|
|
|
|
|
@@ -1034,6 +1110,7 @@ bool parseProject( |
|
|
|
{ |
|
|
|
QJsonArray pages; |
|
|
|
QJsonArray logics; |
|
|
|
QJsonArray data_points; |
|
|
|
if (!readString( |
|
|
|
object, |
|
|
|
"formatVersion", |
|
|
|
@@ -1054,7 +1131,8 @@ bool parseProject( |
|
|
|
if (!readString(object, "id", "project", &project->metadata.id, state) |
|
|
|
|| !readString(object, "name", "project", &project->metadata.name, state) |
|
|
|
|| !readArray(object, "hmiPages", "project", &pages, state) |
|
|
|
|| !readArray(object, "controlLogics", "project", &logics, state)) |
|
|
|
|| !readArray(object, "controlLogics", "project", &logics, state) |
|
|
|
|| !readArray(object, "dataPoints", "project", &data_points, state)) |
|
|
|
{ |
|
|
|
return false; |
|
|
|
} |
|
|
|
@@ -1101,6 +1179,26 @@ bool parseProject( |
|
|
|
} |
|
|
|
project->controlLogics.push_back(std::move(logic)); |
|
|
|
} |
|
|
|
project->dataPoints.reserve(static_cast<std::size_t>(data_points.size())); |
|
|
|
for (int index = 0; index < data_points.size(); ++index) |
|
|
|
{ |
|
|
|
if (!data_points.at(index).isObject()) |
|
|
|
{ |
|
|
|
return state->fail( |
|
|
|
ProjectStorageError::InvalidField, |
|
|
|
"project.dataPoints items must be objects"); |
|
|
|
} |
|
|
|
DataPoint data_point{RegisterAddress{RegisterArea::M, 0}, {}, {}}; |
|
|
|
if (!parseDataPoint( |
|
|
|
data_points.at(index).toObject(), |
|
|
|
"project.dataPoints[" + std::to_string(index) + ']', |
|
|
|
&data_point, |
|
|
|
state)) |
|
|
|
{ |
|
|
|
return false; |
|
|
|
} |
|
|
|
project->dataPoints.push_back(std::move(data_point)); |
|
|
|
} |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
|