Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/* Copyright 2025 CyberTech Labs Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License. */

#pragma once
#include "twoDModel/twoDModelDeclSpec.h"
#include <kitBase/robotModel/portInfo.h>
#include <QDomElement>

namespace twoDModel {

namespace robotModel {
class TwoDRobotModel;
}

namespace model {

/// For different KITs, such as the EV3 and TRIK twoDModel, many things are expressed through the same
/// entities. For example, both models have two wheels with a motor and an encoder attached to them,
/// as well as sensors with a common interface, and so on. Despite the fact that these sensors or other
/// devices have common interfaces (which are ultimately inherited from the ScalarSensor or VectorSensor
/// class or the Device class), it is necessary to write new code due to the difference in entity names
/// for different KITs. In constraints, this happens, for example, when accessing objects through event chains,
/// such as robot1.M4 or robot1.A. Ports already have alias semantics, but the ability to configure ports from the
/// UI, check their availability, and more complicates the situation. The hash function for ports in containers
/// also only considers the name and direction. It is proposed to solve the problem at the worldModel.xml level.
class TWO_D_MODEL_EXPORT AliasConfiguration
{
Q_DISABLE_COPY_MOVE(AliasConfiguration)
public:
explicit AliasConfiguration(twoDModel::robotModel::TwoDRobotModel &robotModel);
~AliasConfiguration() = default;
void serialize(QDomElement &element) const;
void deserialize(const QDomElement &element);

/// Alias is a port property, so if the user changes the device on the port, it should not affect
/// the list of aliases specified in WorldModel.xml
/// User aliases cannot have a name that is already occupied by another port,
/// to avoid ambiguous situations that may arise when debugging programs
QStringList getAliases(const kitBase::robotModel::PortInfo &portInfo) const;

/// It is necessary for the correct serialization of devices in the 2D model. The fact is that the
/// classes that store information about devices and ports use hash functions for the port that take into account
/// only the name and direction of the port, and the information about the alises is lost. For example, a widget
/// for selecting a motor for the left and right wheels that has default values or a sensor configurator.
/// At the same time, they are responsible for serialization.
kitBase::robotModel::PortInfo createFromPort(const kitBase::robotModel::PortInfo &port) const;

/// needSerialize: At the moment, information about custom sensors and wheels remains in their
/// blocks in WorldModel.xml, but this class is still responsible for storing aliases.
void addAliases(const kitBase::robotModel::PortInfo &portInfo, bool needSerialize = false);
private:
struct AliasInfo {
QStringList mNames{};
bool mNeedSerliaze {};
};

twoDModel::robotModel::TwoDRobotModel &mRobotModel;
QHash<kitBase::robotModel::PortInfo, AliasInfo> mAllowedAliases;
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@

#include "twoDModel/robotModel/twoDRobotModel.h"
#include "sensorsConfiguration.h"

#include "twoDModel/twoDModelDeclSpec.h"

class QGraphicsItem;
Expand All @@ -37,6 +36,7 @@ namespace model {
class TwoDRobotModelParameters;
class MetricCoordinateSystem;
class Settings;
class AliasConfiguration;
namespace physics {
class PhysicsEngineBase;
}
Expand Down Expand Up @@ -81,7 +81,7 @@ class TWO_D_MODEL_EXPORT RobotModel : public QObject
, twoDModel::model::MetricCoordinateSystem *metricSystem
, QObject *parent = nullptr);

~RobotModel();
~RobotModel() override;

void reinit();
void reinitMotors();
Expand All @@ -91,10 +91,12 @@ class TWO_D_MODEL_EXPORT RobotModel : public QObject
void stopRobot();
void playSound(int timeInMs);

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

SensorsConfiguration &configuration();

QSharedPointer<AliasConfiguration> aliasConfiguration();

/// Returns information about the robot`s left wheel state (its size, speed, encoder value, etc).
const Wheel &leftWheel() const;

Expand Down Expand Up @@ -199,7 +201,7 @@ public Q_SLOTS:
private:
QVector2D robotDirectionVector() const;

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

void countNewForces();
void countBeep();
Expand Down Expand Up @@ -231,6 +233,7 @@ public Q_SLOTS:
twoDModel::robotModel::TwoDRobotModel &mRobotModel;
// Takes ownership
QPointer<TwoDRobotModelParameters> mRobotModelParameters;
QSharedPointer<AliasConfiguration> mAliasConfiguration;
// Takes ownership.
QPointer<SensorsConfiguration> mSensorsConfiguration;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@
#include <QtXml/QDomDocument>
#include <QPointer>
#include <kitBase/devicesConfigurationProvider.h>
#include "twoDModel/robotModel/twoDRobotModel.h"

#include "twoDModel/twoDModelDeclSpec.h"

namespace twoDModel {
namespace model {

class AliasConfiguration;
class MetricCoordinateSystem;

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

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

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

QPointF defaultPosition(const kitBase::robotModel::DeviceInfo &device) const;

QSharedPointer<twoDModel::model::AliasConfiguration> mAliasConfiguration;
QSizeF mRobotSize;
QString mRobotId;
QHash<kitBase::robotModel::PortInfo, SensorInfo> mSensorsInfo;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "details/constraintsParser.h"
#include "details/event.h"
#include "twoDModel/engine/model/model.h"
#include "twoDModel/engine/model/aliasConfiguration.h"
#include "src/engine/items/wallItem.h"
#include "src/engine/items/skittleItem.h"
#include "src/engine/items/ballItem.h"
Expand Down Expand Up @@ -244,14 +245,20 @@ void ConstraintsChecker::bindRobotObject(twoDModel::model::RobotModel * const ro
, this, [=](const kitBase::robotModel::PortInfo &port, bool isLoading)
{
Q_UNUSED(isLoading)
mObjects.remove(portName(robotId, robot, port));
for (auto &&name: portNames(robotId, robot, port)) {
mObjects.remove(name);
}
});
}

void ConstraintsChecker::bindDeviceObject(const QString &robotId
, model::RobotModel * const robot, const kitBase::robotModel::PortInfo &port)
{
mObjects[portName(robotId, robot, port)] = robot->info().configuration().device(port);
const auto &device = robot->info().configuration().device(port);
const auto &names = portNames(robotId, robot, port);
for (auto &&name: names) {
mObjects[name] = device;
}
}

QString ConstraintsChecker::firstUnusedRobotId() const
Expand All @@ -264,6 +271,22 @@ QString ConstraintsChecker::firstUnusedRobotId() const
return "robot" + QString::number(id);
}

QStringList ConstraintsChecker::portNames(const QString &robotId
, model::RobotModel * const robot
, const kitBase::robotModel::PortInfo &port) const
{
QStringList result;
const auto &mainPort = portName(robotId, robot, port);
result.append(mainPort);

auto aliasConfiguration = robot->aliasConfiguration();
const auto &userAliases = aliasConfiguration->getAliases(port);
for (auto &&userAlias: userAliases) {
result.append(QString("%1.%2").arg(robotId, userAlias));
}
return result;
}

QString ConstraintsChecker::portName(const QString &robotId
, model::RobotModel * const robot, const kitBase::robotModel::PortInfo &port) const
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class ErrorReporterInterface;
namespace kitBase {
namespace robotModel {
class PortInfo;
class DeviceInfo;
}
}

Expand Down Expand Up @@ -116,7 +117,9 @@ class ConstraintsChecker : public QObject
QString portName(const QString &robotId
, model::RobotModel * const robot
, const kitBase::robotModel::PortInfo &port) const;

QStringList portNames(const QString &robotId
, model::RobotModel * const robot
, const kitBase::robotModel::PortInfo &port) const;
void programStarted();
void programFinished(qReal::interpretation::StopReason reason);
void prepareVariables();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/* Copyright 2025 CyberTech Labs Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License. */

#include "twoDModel/engine/model/aliasConfiguration.h"
#include "twoDModel/robotModel/twoDRobotModel.h"

using namespace twoDModel::model;
using namespace kitBase::robotModel;

AliasConfiguration::AliasConfiguration(twoDModel::robotModel::TwoDRobotModel &robotModel)
: mRobotModel(robotModel) {}

void AliasConfiguration::deserialize(const QDomElement &element)
{
mAllowedAliases.clear();
// The configurator block is used to set aliases for non-configurable ports,
// as the syntax for aliases for the configurator sensor and wheels already
// exists in worldModel.xml. However, they can also be used in this block,
// but the information from their own blocks will take precedence and
// overwrite this information.
auto &&configurators = element.firstChildElement("configurators");
for (auto &&configurator = configurators.firstChildElement("configurator");
!configurator.isNull();
configurator = configurator.nextSiblingElement("configurator"))
{
auto &&portInfo = PortInfo::fromString(configurator.attribute("port"));
addAliases(portInfo, true);
}
}

QStringList AliasConfiguration::getAliases(const kitBase::robotModel::PortInfo &portInfo) const
{
const auto &aliasesIt = mAllowedAliases.find(portInfo);
if (aliasesIt == mAllowedAliases.end()) {
return {};
}
return aliasesIt.value().mNames;
}

PortInfo AliasConfiguration::createFromPort(const PortInfo &port) const
{
return PortInfo {
port.name(),
port.userFriendlyName(),
port.direction(),
getAliases(port),
port.reservedVariable(),
port.reservedVariableType()};
}

void AliasConfiguration::addAliases(const kitBase::robotModel::PortInfo &portInfo, bool needSerialize)
{
QStringList result;
const auto &portName = portInfo.name();
const auto &aliases = portInfo.nameAliases();
for (auto &&alias: aliases) {
bool found = {};
for (auto &&otherPort : mRobotModel.availablePorts()) {
if ((portName != otherPort.name())
&& (otherPort.name() == alias)) {
found = true;
break;
}
}
if (!found) {
result.append(alias);
}
}
mAllowedAliases[portInfo] = AliasInfo{result, needSerialize};
}

void AliasConfiguration::serialize(QDomElement &element) const
{
auto &&configurators = element.ownerDocument().createElement("configurators");

bool exist = false;
for (auto it = mAllowedAliases.begin(), end = mAllowedAliases.end(); it != end; ++it) {
if (!it.value().mNeedSerliaze) {
continue;
}
exist = true;
auto &&configurator = element.ownerDocument().createElement("configurator");
auto &&port = it.key();
auto &&portInfo = createFromPort(port);
configurator.setAttribute("port", portInfo.toString());
configurators.appendChild(configurator);
}
if (exist) {
element.appendChild(configurators);
}
}
Loading