Skip to content

Commit cbc2cdc

Browse files
authored
Add alias support to the constraints checker (#2072)
* Add alias support to the constraints checker * Fix Vera++ issues * Fix a stupid bug * Fix Clang-Tidy warnings
1 parent f09c415 commit cbc2cdc

File tree

13 files changed

+277
-47
lines changed

13 files changed

+277
-47
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/* Copyright 2025 CyberTech Labs Ltd.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License. */
14+
15+
#pragma once
16+
#include "twoDModel/twoDModelDeclSpec.h"
17+
#include <kitBase/robotModel/portInfo.h>
18+
#include <QDomElement>
19+
20+
namespace twoDModel {
21+
22+
namespace robotModel {
23+
class TwoDRobotModel;
24+
}
25+
26+
namespace model {
27+
28+
/// For different KITs, such as the EV3 and TRIK twoDModel, many things are expressed through the same
29+
/// entities. For example, both models have two wheels with a motor and an encoder attached to them,
30+
/// as well as sensors with a common interface, and so on. Despite the fact that these sensors or other
31+
/// devices have common interfaces (which are ultimately inherited from the ScalarSensor or VectorSensor
32+
/// class or the Device class), it is necessary to write new code due to the difference in entity names
33+
/// for different KITs. In constraints, this happens, for example, when accessing objects through event chains,
34+
/// such as robot1.M4 or robot1.A. Ports already have alias semantics, but the ability to configure ports from the
35+
/// UI, check their availability, and more complicates the situation. The hash function for ports in containers
36+
/// also only considers the name and direction. It is proposed to solve the problem at the worldModel.xml level.
37+
class TWO_D_MODEL_EXPORT AliasConfiguration
38+
{
39+
Q_DISABLE_COPY_MOVE(AliasConfiguration)
40+
public:
41+
explicit AliasConfiguration(twoDModel::robotModel::TwoDRobotModel &robotModel);
42+
~AliasConfiguration() = default;
43+
void serialize(QDomElement &element) const;
44+
void deserialize(const QDomElement &element);
45+
46+
/// Alias is a port property, so if the user changes the device on the port, it should not affect
47+
/// the list of aliases specified in WorldModel.xml
48+
/// User aliases cannot have a name that is already occupied by another port,
49+
/// to avoid ambiguous situations that may arise when debugging programs
50+
QStringList getAliases(const kitBase::robotModel::PortInfo &portInfo) const;
51+
52+
/// It is necessary for the correct serialization of devices in the 2D model. The fact is that the
53+
/// classes that store information about devices and ports use hash functions for the port that take into account
54+
/// only the name and direction of the port, and the information about the alises is lost. For example, a widget
55+
/// for selecting a motor for the left and right wheels that has default values or a sensor configurator.
56+
/// At the same time, they are responsible for serialization.
57+
kitBase::robotModel::PortInfo createFromPort(const kitBase::robotModel::PortInfo &port) const;
58+
59+
/// needSerialize: At the moment, information about custom sensors and wheels remains in their
60+
/// blocks in WorldModel.xml, but this class is still responsible for storing aliases.
61+
void addAliases(const kitBase::robotModel::PortInfo &portInfo, bool needSerialize = false);
62+
private:
63+
struct AliasInfo {
64+
QStringList mNames{};
65+
bool mNeedSerliaze {};
66+
};
67+
68+
twoDModel::robotModel::TwoDRobotModel &mRobotModel;
69+
QHash<kitBase::robotModel::PortInfo, AliasInfo> mAllowedAliases;
70+
};
71+
}
72+
}

plugins/robots/common/twoDModel/include/twoDModel/engine/model/robotModel.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222

2323
#include "twoDModel/robotModel/twoDRobotModel.h"
2424
#include "sensorsConfiguration.h"
25-
2625
#include "twoDModel/twoDModelDeclSpec.h"
2726

2827
class QGraphicsItem;
@@ -37,6 +36,7 @@ namespace model {
3736
class TwoDRobotModelParameters;
3837
class MetricCoordinateSystem;
3938
class Settings;
39+
class AliasConfiguration;
4040
namespace physics {
4141
class PhysicsEngineBase;
4242
}
@@ -81,7 +81,7 @@ class TWO_D_MODEL_EXPORT RobotModel : public QObject
8181
, twoDModel::model::MetricCoordinateSystem *metricSystem
8282
, QObject *parent = nullptr);
8383

84-
~RobotModel();
84+
~RobotModel() override;
8585

8686
void reinit();
8787
void reinitMotors();
@@ -91,10 +91,12 @@ class TWO_D_MODEL_EXPORT RobotModel : public QObject
9191
void stopRobot();
9292
void playSound(int timeInMs);
9393

94-
Q_INVOKABLE void setNewMotor(int speed, uint degrees, const kitBase::robotModel::PortInfo &port, bool breakMode);
94+
Q_INVOKABLE void setNewMotor(int speed, int degrees, const kitBase::robotModel::PortInfo &port, bool breakMode);
9595

9696
SensorsConfiguration &configuration();
9797

98+
QSharedPointer<AliasConfiguration> aliasConfiguration();
99+
98100
/// Returns information about the robot`s left wheel state (its size, speed, encoder value, etc).
99101
const Wheel &leftWheel() const;
100102

@@ -199,7 +201,7 @@ public Q_SLOTS:
199201
private:
200202
QVector2D robotDirectionVector() const;
201203

202-
Wheel *initMotor(int radius, int speed, uint64_t degrees, const kitBase::robotModel::PortInfo &port, bool isUsed);
204+
Wheel *initMotor(int radius, int speed, int degrees, const kitBase::robotModel::PortInfo &port, bool isUsed);
203205

204206
void countNewForces();
205207
void countBeep();
@@ -231,6 +233,7 @@ public Q_SLOTS:
231233
twoDModel::robotModel::TwoDRobotModel &mRobotModel;
232234
// Takes ownership
233235
QPointer<TwoDRobotModelParameters> mRobotModelParameters;
236+
QSharedPointer<AliasConfiguration> mAliasConfiguration;
234237
// Takes ownership.
235238
QPointer<SensorsConfiguration> mSensorsConfiguration;
236239

plugins/robots/common/twoDModel/include/twoDModel/engine/model/sensorsConfiguration.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@
2020
#include <QtXml/QDomDocument>
2121
#include <QPointer>
2222
#include <kitBase/devicesConfigurationProvider.h>
23+
#include "twoDModel/robotModel/twoDRobotModel.h"
2324

2425
#include "twoDModel/twoDModelDeclSpec.h"
2526

2627
namespace twoDModel {
2728
namespace model {
2829

30+
class AliasConfiguration;
2931
class MetricCoordinateSystem;
3032

3133
/// Represents position and direction of simulated sensors relative to robot.
@@ -35,6 +37,7 @@ class TWO_D_MODEL_EXPORT SensorsConfiguration : public QObject, public kitBase::
3537

3638
public:
3739
SensorsConfiguration(twoDModel::model::MetricCoordinateSystem *metricSystem,
40+
QSharedPointer<twoDModel::model::AliasConfiguration> aliasConfiguration,
3841
const QString &robotModelName, QSizeF robotSize, QObject *parent = nullptr);
3942

4043
void setPosition(const kitBase::robotModel::PortInfo &port, QPointF position);
@@ -81,7 +84,7 @@ public Q_SLOTS:
8184
, Reason reason) override;
8285

8386
QPointF defaultPosition(const kitBase::robotModel::DeviceInfo &device) const;
84-
87+
QSharedPointer<twoDModel::model::AliasConfiguration> mAliasConfiguration;
8588
QSizeF mRobotSize;
8689
QString mRobotId;
8790
QHash<kitBase::robotModel::PortInfo, SensorInfo> mSensorsInfo;

plugins/robots/common/twoDModel/src/engine/constraints/constraintsChecker.cpp

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "details/constraintsParser.h"
2323
#include "details/event.h"
2424
#include "twoDModel/engine/model/model.h"
25+
#include "twoDModel/engine/model/aliasConfiguration.h"
2526
#include "src/engine/items/wallItem.h"
2627
#include "src/engine/items/skittleItem.h"
2728
#include "src/engine/items/ballItem.h"
@@ -244,14 +245,20 @@ void ConstraintsChecker::bindRobotObject(twoDModel::model::RobotModel * const ro
244245
, this, [=](const kitBase::robotModel::PortInfo &port, bool isLoading)
245246
{
246247
Q_UNUSED(isLoading)
247-
mObjects.remove(portName(robotId, robot, port));
248+
for (auto &&name: portNames(robotId, robot, port)) {
249+
mObjects.remove(name);
250+
}
248251
});
249252
}
250253

251254
void ConstraintsChecker::bindDeviceObject(const QString &robotId
252255
, model::RobotModel * const robot, const kitBase::robotModel::PortInfo &port)
253256
{
254-
mObjects[portName(robotId, robot, port)] = robot->info().configuration().device(port);
257+
const auto &device = robot->info().configuration().device(port);
258+
const auto &names = portNames(robotId, robot, port);
259+
for (auto &&name: names) {
260+
mObjects[name] = device;
261+
}
255262
}
256263

257264
QString ConstraintsChecker::firstUnusedRobotId() const
@@ -264,6 +271,22 @@ QString ConstraintsChecker::firstUnusedRobotId() const
264271
return "robot" + QString::number(id);
265272
}
266273

274+
QStringList ConstraintsChecker::portNames(const QString &robotId
275+
, model::RobotModel * const robot
276+
, const kitBase::robotModel::PortInfo &port) const
277+
{
278+
QStringList result;
279+
const auto &mainPort = portName(robotId, robot, port);
280+
result.append(mainPort);
281+
282+
auto aliasConfiguration = robot->aliasConfiguration();
283+
const auto &userAliases = aliasConfiguration->getAliases(port);
284+
for (auto &&userAlias: userAliases) {
285+
result.append(QString("%1.%2").arg(robotId, userAlias));
286+
}
287+
return result;
288+
}
289+
267290
QString ConstraintsChecker::portName(const QString &robotId
268291
, model::RobotModel * const robot, const kitBase::robotModel::PortInfo &port) const
269292
{

plugins/robots/common/twoDModel/src/engine/constraints/constraintsChecker.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class ErrorReporterInterface;
2828
namespace kitBase {
2929
namespace robotModel {
3030
class PortInfo;
31+
class DeviceInfo;
3132
}
3233
}
3334

@@ -116,7 +117,9 @@ class ConstraintsChecker : public QObject
116117
QString portName(const QString &robotId
117118
, model::RobotModel * const robot
118119
, const kitBase::robotModel::PortInfo &port) const;
119-
120+
QStringList portNames(const QString &robotId
121+
, model::RobotModel * const robot
122+
, const kitBase::robotModel::PortInfo &port) const;
120123
void programStarted();
121124
void programFinished(qReal::interpretation::StopReason reason);
122125
void prepareVariables();
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/* Copyright 2025 CyberTech Labs Ltd.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License. */
14+
15+
#include "twoDModel/engine/model/aliasConfiguration.h"
16+
#include "twoDModel/robotModel/twoDRobotModel.h"
17+
18+
using namespace twoDModel::model;
19+
using namespace kitBase::robotModel;
20+
21+
AliasConfiguration::AliasConfiguration(twoDModel::robotModel::TwoDRobotModel &robotModel)
22+
: mRobotModel(robotModel) {}
23+
24+
void AliasConfiguration::deserialize(const QDomElement &element)
25+
{
26+
mAllowedAliases.clear();
27+
// The configurator block is used to set aliases for non-configurable ports,
28+
// as the syntax for aliases for the configurator sensor and wheels already
29+
// exists in worldModel.xml. However, they can also be used in this block,
30+
// but the information from their own blocks will take precedence and
31+
// overwrite this information.
32+
auto &&configurators = element.firstChildElement("configurators");
33+
for (auto &&configurator = configurators.firstChildElement("configurator");
34+
!configurator.isNull();
35+
configurator = configurator.nextSiblingElement("configurator"))
36+
{
37+
auto &&portInfo = PortInfo::fromString(configurator.attribute("port"));
38+
addAliases(portInfo, true);
39+
}
40+
}
41+
42+
QStringList AliasConfiguration::getAliases(const kitBase::robotModel::PortInfo &portInfo) const
43+
{
44+
const auto &aliasesIt = mAllowedAliases.find(portInfo);
45+
if (aliasesIt == mAllowedAliases.end()) {
46+
return {};
47+
}
48+
return aliasesIt.value().mNames;
49+
}
50+
51+
PortInfo AliasConfiguration::createFromPort(const PortInfo &port) const
52+
{
53+
return PortInfo {
54+
port.name(),
55+
port.userFriendlyName(),
56+
port.direction(),
57+
getAliases(port),
58+
port.reservedVariable(),
59+
port.reservedVariableType()};
60+
}
61+
62+
void AliasConfiguration::addAliases(const kitBase::robotModel::PortInfo &portInfo, bool needSerialize)
63+
{
64+
QStringList result;
65+
const auto &portName = portInfo.name();
66+
const auto &aliases = portInfo.nameAliases();
67+
for (auto &&alias: aliases) {
68+
bool found = {};
69+
for (auto &&otherPort : mRobotModel.availablePorts()) {
70+
if ((portName != otherPort.name())
71+
&& (otherPort.name() == alias)) {
72+
found = true;
73+
break;
74+
}
75+
}
76+
if (!found) {
77+
result.append(alias);
78+
}
79+
}
80+
mAllowedAliases[portInfo] = AliasInfo{result, needSerialize};
81+
}
82+
83+
void AliasConfiguration::serialize(QDomElement &element) const
84+
{
85+
auto &&configurators = element.ownerDocument().createElement("configurators");
86+
87+
bool exist = false;
88+
for (auto it = mAllowedAliases.begin(), end = mAllowedAliases.end(); it != end; ++it) {
89+
if (!it.value().mNeedSerliaze) {
90+
continue;
91+
}
92+
exist = true;
93+
auto &&configurator = element.ownerDocument().createElement("configurator");
94+
auto &&port = it.key();
95+
auto &&portInfo = createFromPort(port);
96+
configurator.setAttribute("port", portInfo.toString());
97+
configurators.appendChild(configurator);
98+
}
99+
if (exist) {
100+
element.appendChild(configurators);
101+
}
102+
}

0 commit comments

Comments
 (0)