init
This commit is contained in:
commit
8d45088064
20 changed files with 974 additions and 0 deletions
3
.clangd
Normal file
3
.clangd
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
CompileFlags:
|
||||||
|
Add:
|
||||||
|
- -std=c++20
|
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/.vscode
|
||||||
|
/build
|
35
CMakeLists.txt
Normal file
35
CMakeLists.txt
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
cmake_minimum_required(VERSION 3.15)
|
||||||
|
|
||||||
|
project(robotCommand)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||||
|
#set(CMAKE_VERBOSE_MAKEFILE ON)
|
||||||
|
|
||||||
|
set(gcc_like_cxx "$<COMPILE_LANG_AND_ID:CXX,ARMClang,AppleClang,Clang,GNU,LCC>")
|
||||||
|
set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>")
|
||||||
|
|
||||||
|
add_executable(robotCommand
|
||||||
|
src/main.cpp
|
||||||
|
src/robot.cpp
|
||||||
|
src/jsonrpctcpclient.cpp
|
||||||
|
src/PosTracker.cpp
|
||||||
|
src/PosHistory.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
find_package(jsoncpp REQUIRED)
|
||||||
|
find_package(SFML COMPONENTS graphics window system REQUIRED)
|
||||||
|
target_link_libraries(robotCommand jsoncpp_lib sfml-graphics sfml-window sfml-system)
|
||||||
|
if (WIN32)
|
||||||
|
target_link_libraries(robotCommand ws2_32)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
target_compile_options(robotCommand PRIVATE
|
||||||
|
"$<${gcc_like_cxx}:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>"
|
||||||
|
"$<${msvc_cxx}:-W3>"
|
||||||
|
)
|
||||||
|
|
||||||
|
#add_compile_definitions(JSONRPC_DEBUG)
|
||||||
|
if (WIN32)
|
||||||
|
add_compile_definitions(_WIN32_WINNT=0x0501)
|
||||||
|
endif ()
|
14
README.md
Normal file
14
README.md
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
Setup
|
||||||
|
=====
|
||||||
|
|
||||||
|
- from https://gitlab.cri.epita.fr/jeremie.graulle/ssie-s9-robot-command create a personal fork
|
||||||
|
to be able to commit
|
||||||
|
- clone your fork on your computer:
|
||||||
|
`git clone git@gitlab.cri.epita.fr:<name>/ssie-s9-robot-command.git`
|
||||||
|
- open VS code from this fork: code . &
|
||||||
|
- Build and Run
|
||||||
|
|
||||||
|
Step
|
||||||
|
====
|
||||||
|
|
||||||
|
Update the main of this projet to be able to follow the line displayed on de default simulator map.
|
10
build.sh
Executable file
10
build.sh
Executable file
|
@ -0,0 +1,10 @@
|
||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
cd "$(dirname "$(realpath "$0")")"
|
||||||
|
|
||||||
|
cp -r . /tmp/build-tp4-sim
|
||||||
|
cd /tmp/build-tp4-sim
|
||||||
|
rm -fr /tmp/build-tp4-sim/.git
|
||||||
|
|
||||||
|
cmake .
|
||||||
|
make -j$(nproc)
|
11
run.sh
Executable file
11
run.sh
Executable file
|
@ -0,0 +1,11 @@
|
||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
cd "$(dirname "$(realpath "$0")")"
|
||||||
|
|
||||||
|
if ! [ -f /tmp/build-tp4-sim/robotCommand ]
|
||||||
|
then ./build.sh
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd /tmp/build-tp4-sim/
|
||||||
|
/tmp/build-tp4-sim/robotCommand $@
|
||||||
|
|
25
src/PosHistory.cpp
Normal file
25
src/PosHistory.cpp
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#include "PosHistory.hpp"
|
||||||
|
#include <SFML/Graphics/Vertex.hpp>
|
||||||
|
#include <SFML/System/Vector2.hpp>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
bool rounded_positions_equal(sf::Vector2f& a, sf::Vector2f& b) {
|
||||||
|
if (int(a.x) != int(b.x)) return false;
|
||||||
|
if (int(a.y) != int(b.y)) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PosHistory::append(sf::Vector2f point) {
|
||||||
|
if (!shouldInsert(point)) return;
|
||||||
|
_points[_insertionIndex] = sf::Vertex(point);
|
||||||
|
_insertionIndex += 1;
|
||||||
|
if (_insertionIndex >= _capacity) _insertionIndex = 0; // note : loops over to start of the array.
|
||||||
|
_lastInsertion = std::optional(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PosHistory::shouldInsert(sf::Vector2f& point) {
|
||||||
|
if (!_lastInsertion.has_value()) return true;
|
||||||
|
auto last = _lastInsertion.value();
|
||||||
|
if (rounded_positions_equal(last, point)) return false;
|
||||||
|
return true;
|
||||||
|
}
|
27
src/PosHistory.hpp
Normal file
27
src/PosHistory.hpp
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SFML/Graphics/PrimitiveType.hpp>
|
||||||
|
#include <SFML/Graphics/VertexArray.hpp>
|
||||||
|
#include <SFML/System/Vector2.hpp>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
class PosHistory {
|
||||||
|
sf::VertexArray _points;
|
||||||
|
std::optional<sf::Vector2f> _lastInsertion;
|
||||||
|
size_t _insertionIndex;
|
||||||
|
size_t _capacity;
|
||||||
|
public:
|
||||||
|
inline PosHistory(size_t capacity) :
|
||||||
|
_points(sf::VertexArray(sf::PrimitiveType::Points, capacity)),
|
||||||
|
_lastInsertion(std::optional<sf::Vector2f>()),
|
||||||
|
_insertionIndex(0),
|
||||||
|
_capacity(capacity)
|
||||||
|
{
|
||||||
|
// for (size_t i = 0; i < capacity_; i += 1) append(sf::Vector2f(-1, -1));
|
||||||
|
}
|
||||||
|
void append(sf::Vector2f point);
|
||||||
|
inline sf::VertexArray& vertices() { return _points; }
|
||||||
|
private:
|
||||||
|
bool shouldInsert(sf::Vector2f& point);
|
||||||
|
};
|
28
src/PosTracker.cpp
Normal file
28
src/PosTracker.cpp
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#include "PosTracker.hpp"
|
||||||
|
|
||||||
|
using sf::Vector2f;
|
||||||
|
using sf::Transform;
|
||||||
|
|
||||||
|
void PosTracker::reset(Vector2f initialPos, float initialRot) {
|
||||||
|
transform = transformFromPosRot(initialPos, initialRot);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PosTracker::update(float newLeftValue, float newRightValue) {
|
||||||
|
auto leftDistance = (newLeftValue - _lastLeftValue) * wheelIncrementDistance();
|
||||||
|
auto rightDistance = (newRightValue - _lastRightValue) * wheelIncrementDistance();
|
||||||
|
updateTransform(leftDistance, rightDistance);
|
||||||
|
_lastLeftValue = newLeftValue;
|
||||||
|
_lastRightValue = newRightValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PosTracker::updateTransform(float leftDistance, float rightDistance) {
|
||||||
|
float linearStep = (leftDistance + rightDistance) / 2.0;
|
||||||
|
float angularStep = (leftDistance - rightDistance) / _wheelDistance * 180.0 / std::numbers::pi;
|
||||||
|
|
||||||
|
auto orientation = Transform(sf::Transform().rotate(transform.getRotation()));
|
||||||
|
auto new_ = transformFromPosRot(
|
||||||
|
transform.getPosition() + orientation.transformPoint(sf::Vector2f(linearStep, 0.0)),
|
||||||
|
transform.getRotation() + angularStep
|
||||||
|
);
|
||||||
|
transform = new_;
|
||||||
|
}
|
42
src/PosTracker.hpp
Normal file
42
src/PosTracker.hpp
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SFML/Graphics/RectangleShape.hpp>
|
||||||
|
#include <SFML/Graphics/RenderWindow.hpp>
|
||||||
|
#include <SFML/Graphics/Transform.hpp>
|
||||||
|
#include <SFML/Graphics/Transformable.hpp>
|
||||||
|
#include <SFML/System/Vector2.hpp>
|
||||||
|
#include <SFML/Window/Event.hpp>
|
||||||
|
|
||||||
|
inline sf::Transformable transformFromPosRot(sf::Vector2f position, float rotation) {
|
||||||
|
auto result = sf::Transformable();
|
||||||
|
result.setPosition(position);
|
||||||
|
result.setRotation(rotation);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
class PosTracker {
|
||||||
|
/// Wheel encoder values.
|
||||||
|
float _wheelDistance, _wheelDiameter;
|
||||||
|
float _wheelStepsPerTurn;
|
||||||
|
float _lastLeftValue, _lastRightValue;
|
||||||
|
sf::Transformable transform;
|
||||||
|
public:
|
||||||
|
PosTracker(sf::Vector2f initialPos, float initialRot, float wheelDistance, float wheelDiameter, float wheelStepsPerTurn) :
|
||||||
|
_wheelDistance(wheelDistance),
|
||||||
|
_wheelDiameter(wheelDiameter),
|
||||||
|
_wheelStepsPerTurn(wheelStepsPerTurn),
|
||||||
|
_lastLeftValue(0.),
|
||||||
|
_lastRightValue(0.),
|
||||||
|
transform(transformFromPosRot(initialPos, initialRot))
|
||||||
|
{}
|
||||||
|
void reset(sf::Vector2f initialPos, float initialRot);
|
||||||
|
void update(float newLeftValue, float newRightValue);
|
||||||
|
|
||||||
|
inline sf::Vector2f getPosition() const { return transform.getPosition(); }
|
||||||
|
inline float getRotation() const { return transform.getRotation(); }
|
||||||
|
private:
|
||||||
|
inline float wheelIncrementDistance() const {
|
||||||
|
return _wheelDiameter * std::numbers::pi / _wheelStepsPerTurn;
|
||||||
|
}
|
||||||
|
void updateTransform(float leftDistance, float rightDistance);
|
||||||
|
};
|
25
src/filter.cpp
Normal file
25
src/filter.cpp
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#include <cmath>
|
||||||
|
#include "filter.hpp"
|
||||||
|
|
||||||
|
void MotorJitterFilter::set(float value) {
|
||||||
|
_value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
float MotorJitterFilter::get(float delta) {
|
||||||
|
update(delta);
|
||||||
|
if (_stateIsOn) return 1;
|
||||||
|
else return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
float MotorJitterFilter::delayOfState() {
|
||||||
|
float result = 0;
|
||||||
|
if (_stateIsOn) result = _delay * _value;
|
||||||
|
else result = _delay * (1 - _value);
|
||||||
|
return std::fmax(result, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MotorJitterFilter::update(float delta) {
|
||||||
|
_delaySinceState += delta;
|
||||||
|
if (_delaySinceState < delayOfState()) return;
|
||||||
|
_stateIsOn = !_stateIsOn;
|
||||||
|
}
|
17
src/filter.hpp
Normal file
17
src/filter.hpp
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
class MotorJitterFilter {
|
||||||
|
public:
|
||||||
|
inline MotorJitterFilter(float delay) : _delay(delay), _delaySinceState(0), _stateIsOn(false), _value(0) {}
|
||||||
|
void set(float value);
|
||||||
|
float get(float delta);
|
||||||
|
|
||||||
|
private:
|
||||||
|
float _value;
|
||||||
|
float _delay;
|
||||||
|
float _delaySinceState;
|
||||||
|
bool _stateIsOn;
|
||||||
|
|
||||||
|
float delayOfState();
|
||||||
|
void update(float delta);
|
||||||
|
};
|
161
src/jsonrpctcpclient.cpp
Normal file
161
src/jsonrpctcpclient.cpp
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
#include "jsonrpctcpclient.hpp"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
#include <memory>
|
||||||
|
#include <json/json.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <asio/streambuf.hpp>
|
||||||
|
#include <asio/write.hpp>
|
||||||
|
#include <asio/read_until.hpp>
|
||||||
|
#include <asio/read.hpp>
|
||||||
|
#include <asio/buffer.hpp>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
|
||||||
|
JsonRpcTcpClient::JsonRpcTcpClient(const std::string & hostIpAddress, unsigned short tcpPort)
|
||||||
|
: _ioc()
|
||||||
|
, _socket(_ioc)
|
||||||
|
, _jsonRpcId(1)
|
||||||
|
, _tcpStreambuf()
|
||||||
|
, _tcpOutStream(&_tcpStreambuf)
|
||||||
|
, _jsonStreamWriter(nullptr)
|
||||||
|
, _receiveMethodResponseSem(0)
|
||||||
|
, _isStartReceive(false)
|
||||||
|
{
|
||||||
|
Json::StreamWriterBuilder jsonStreamWriterBuilder;
|
||||||
|
jsonStreamWriterBuilder["indentation"] = "";
|
||||||
|
_jsonStreamWriter.reset(jsonStreamWriterBuilder.newStreamWriter());
|
||||||
|
|
||||||
|
_socket.connect(asio::ip::tcp::endpoint(asio::ip::address::from_string(hostIpAddress), tcpPort));
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonRpcTcpClient::~JsonRpcTcpClient()
|
||||||
|
{
|
||||||
|
_socket.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonRpcTcpClient::bindNotification(const std::string & methodName, const std::function<void(Json::Value)> & notificationHandle)
|
||||||
|
{
|
||||||
|
assert(!_isStartReceive);
|
||||||
|
_notificationHandles.insert(std::make_pair(methodName, notificationHandle));
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonRpcTcpClient::startReceive()
|
||||||
|
{
|
||||||
|
_isStartReceive = true;
|
||||||
|
std::thread([](JsonRpcTcpClient * thus){thus->receive();}, this).detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonRpcTcpClient::callNotification(const char * methodName, const Json::Value & params)
|
||||||
|
{
|
||||||
|
// Prepare message
|
||||||
|
Json::Value message;
|
||||||
|
message["jsonrpc"] = "2.0";
|
||||||
|
message["method"] = methodName;
|
||||||
|
message["params"] = params;
|
||||||
|
|
||||||
|
// Send message
|
||||||
|
_jsonStreamWriter->write(message, &_tcpOutStream);
|
||||||
|
_tcpOutStream << static_cast<char>(0x0A);
|
||||||
|
asio::write(_socket, _tcpStreambuf);
|
||||||
|
|
||||||
|
#ifdef JSONRPC_DEBUG
|
||||||
|
// Print message
|
||||||
|
std::cout << "send message ";
|
||||||
|
_jsonStreamWriter->write(message, &std::cout);
|
||||||
|
std::cout << std::endl;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value JsonRpcTcpClient::callMethod(const char * methodName, const Json::Value & param)
|
||||||
|
{
|
||||||
|
// Prepare message
|
||||||
|
Json::Value message;
|
||||||
|
message["jsonrpc"] = "2.0";
|
||||||
|
message["method"] = methodName;
|
||||||
|
message["params"] = param;
|
||||||
|
message["id"] = _jsonRpcId;
|
||||||
|
|
||||||
|
// Send message
|
||||||
|
_jsonStreamWriter->write(message, &_tcpOutStream);
|
||||||
|
_tcpOutStream << static_cast<char>(0x0A);
|
||||||
|
asio::write(_socket, _tcpStreambuf);
|
||||||
|
|
||||||
|
#ifdef JSONRPC_DEBUG
|
||||||
|
// Print message
|
||||||
|
std::cout << "send message ";
|
||||||
|
_jsonStreamWriter->write(message, &std::cout);
|
||||||
|
std::cout << std::endl;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Wait response
|
||||||
|
#ifdef JSONRPC_DEBUG
|
||||||
|
std::cout << "wait response..." << std::endl;
|
||||||
|
#endif
|
||||||
|
_receiveMethodResponseSem.acquire();
|
||||||
|
Json::Value responseJson = _receiveMethodResponseJsonValue;
|
||||||
|
assert(_jsonRpcId == responseJson["id"].asInt());
|
||||||
|
_receiveMethodResponseJsonValue = Json::Value();
|
||||||
|
|
||||||
|
#ifdef JSONRPC_DEBUG
|
||||||
|
// Print response
|
||||||
|
std::cout << "Receive response ";
|
||||||
|
_jsonStreamWriter->write(responseJson, &std::cout);
|
||||||
|
std::cout << std::endl;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
_jsonRpcId++;
|
||||||
|
return responseJson["result"];
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonRpcTcpClient::receive()
|
||||||
|
{
|
||||||
|
asio::streambuf tcpStreambuf;
|
||||||
|
std::istream tcpInStream(&tcpStreambuf);
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
// Wait message
|
||||||
|
asio::read_until(_socket, tcpStreambuf, 0x0A);
|
||||||
|
#ifdef JSONRPC_DEBUG
|
||||||
|
std::cout << "wait message..." << std::endl;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Extract one json message
|
||||||
|
std::string messageJsonStr;
|
||||||
|
std::getline(tcpInStream, messageJsonStr, static_cast<char>(0x0A));
|
||||||
|
|
||||||
|
// Parse this json message
|
||||||
|
Json::CharReaderBuilder builder;
|
||||||
|
JSONCPP_STRING errs;
|
||||||
|
Json::Value messageJson;
|
||||||
|
const std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
|
||||||
|
if (!reader->parse(messageJsonStr.c_str(), messageJsonStr.c_str()+messageJsonStr.size(),
|
||||||
|
&messageJson, &errs))
|
||||||
|
throw std::runtime_error(errs);
|
||||||
|
|
||||||
|
// If method response
|
||||||
|
if (messageJson.isMember("id"))
|
||||||
|
{
|
||||||
|
assert(_receiveMethodResponseJsonValue.isNull());
|
||||||
|
_receiveMethodResponseJsonValue = messageJson;
|
||||||
|
_receiveMethodResponseSem.release();
|
||||||
|
}
|
||||||
|
// If notification
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#ifdef JSONRPC_DEBUG
|
||||||
|
// Print notification
|
||||||
|
std::cout << "Receive notification ";
|
||||||
|
_jsonStreamWriter->write(messageJson, &std::cout);
|
||||||
|
std::cout << std::endl;
|
||||||
|
#endif
|
||||||
|
// Find the notification and execute it
|
||||||
|
auto it = _notificationHandles.find(messageJson["method"].asString());
|
||||||
|
if (it != _notificationHandles.end())
|
||||||
|
{
|
||||||
|
it->second(messageJson["params"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
49
src/jsonrpctcpclient.hpp
Normal file
49
src/jsonrpctcpclient.hpp
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
#ifndef JSONRPCTCPCLIENT_HPP
|
||||||
|
#define JSONRPCTCPCLIENT_HPP
|
||||||
|
|
||||||
|
#include <asio/ip/tcp.hpp>
|
||||||
|
#include <asio/streambuf.hpp>
|
||||||
|
#include <asio/io_context.hpp>
|
||||||
|
#include <semaphore>
|
||||||
|
#include <json/value.h>
|
||||||
|
|
||||||
|
namespace Json
|
||||||
|
{
|
||||||
|
class StreamWriter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class JsonRpcTcpClient
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JsonRpcTcpClient(const std::string & hostIpAddress, unsigned short tcpPort);
|
||||||
|
~JsonRpcTcpClient();
|
||||||
|
|
||||||
|
using NotificationHandle = std::function<void(Json::Value)>;
|
||||||
|
void bindNotification(const std::string & methodName, const NotificationHandle & notificationHandle);
|
||||||
|
|
||||||
|
//! @warning start receive only after bind all notification
|
||||||
|
void startReceive();
|
||||||
|
|
||||||
|
void callNotification(const char * methodName, const Json::Value & param);
|
||||||
|
Json::Value callMethod(const char * methodName, const Json::Value & param);
|
||||||
|
|
||||||
|
private:
|
||||||
|
JsonRpcTcpClient(const JsonRpcTcpClient &) = delete;
|
||||||
|
JsonRpcTcpClient & operator=(const JsonRpcTcpClient &) = delete;
|
||||||
|
|
||||||
|
void receive();
|
||||||
|
|
||||||
|
asio::io_context _ioc;
|
||||||
|
asio::ip::tcp::socket _socket;
|
||||||
|
int _jsonRpcId;
|
||||||
|
asio::streambuf _tcpStreambuf;
|
||||||
|
std::ostream _tcpOutStream;
|
||||||
|
std::unique_ptr<Json::StreamWriter> _jsonStreamWriter;
|
||||||
|
std::binary_semaphore _receiveMethodResponseSem;
|
||||||
|
Json::Value _receiveMethodResponseJsonValue;
|
||||||
|
std::map<std::string, NotificationHandle> _notificationHandles;
|
||||||
|
bool _isStartReceive;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
157
src/main.cpp
Normal file
157
src/main.cpp
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
#include "PosHistory.hpp"
|
||||||
|
#include "PosTracker.hpp"
|
||||||
|
#include "filter.hpp"
|
||||||
|
#include "robot.hpp"
|
||||||
|
#include "utils.hpp"
|
||||||
|
|
||||||
|
#include <SFML/Graphics/PrimitiveType.hpp>
|
||||||
|
#include <SFML/Graphics/RectangleShape.hpp>
|
||||||
|
#include <SFML/Graphics/RenderWindow.hpp>
|
||||||
|
#include <SFML/Graphics/Transform.hpp>
|
||||||
|
#include <SFML/Graphics/Transformable.hpp>
|
||||||
|
#include <SFML/Graphics/Vertex.hpp>
|
||||||
|
#include <SFML/Graphics/VertexArray.hpp>
|
||||||
|
#include <SFML/System/Vector2.hpp>
|
||||||
|
#include <SFML/Window/Event.hpp>
|
||||||
|
#include <SFML/Window/Keyboard.hpp>
|
||||||
|
#include <csignal>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
using sf::Vector2f;
|
||||||
|
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
|
constexpr auto THRESHOLD = 10;
|
||||||
|
|
||||||
|
void controller_follow_border_step(Robot& robot, float speed_factor) {
|
||||||
|
auto line_value = DBG(int(robot.getLineTracksValue(0)));
|
||||||
|
auto detects_line = line_value > THRESHOLD;
|
||||||
|
if (detects_line) robot.setMotorsPower(0.2 * speed_factor, 0.1 * speed_factor);
|
||||||
|
else robot.setMotorsPower(0.1 * speed_factor, 0.2 * speed_factor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void controller_follow_border(Robot &robot, float speed_factor) {
|
||||||
|
while (true) {
|
||||||
|
auto events = {EventType::LINE_TRACKS_IS_DETECTED,EventType::SWITCHS_IS_DETECTED};
|
||||||
|
robot.waitChanged(events, 1s);
|
||||||
|
controller_follow_border_step(robot, speed_factor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// note : Ne marche pas car remplace le stack du code interrompu.
|
||||||
|
static bool do_stop = false;
|
||||||
|
void stop_handler(int) {
|
||||||
|
do_stop = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void event(sf::RenderWindow & window, Robot& robot) {
|
||||||
|
bool do_stop_ = do_stop;
|
||||||
|
sf::Event event;
|
||||||
|
while (window.pollEvent(event)) {
|
||||||
|
if (event.type == sf::Event::Resized) {
|
||||||
|
sf::FloatRect visibleArea(0.f, 0.f, event.size.width, event.size.height);
|
||||||
|
window.setView(sf::View(visibleArea));
|
||||||
|
}
|
||||||
|
if (event.type == sf::Event::Closed) do_stop_ = true;
|
||||||
|
if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape) do_stop_ = true;
|
||||||
|
}
|
||||||
|
if (do_stop_) {
|
||||||
|
robot.setMotorPower(Robot::MotorIndex::LEFT, 0);
|
||||||
|
robot.setMotorPower(Robot::MotorIndex::RIGHT, 0);
|
||||||
|
window.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
constexpr float ROBOT_SIZE = 20.0;
|
||||||
|
constexpr float WHEEL_DIAMETER = 6.5;
|
||||||
|
constexpr float LATICE_COUNT = 20;
|
||||||
|
|
||||||
|
|
||||||
|
class ControlWindow {
|
||||||
|
private:
|
||||||
|
Robot& _robot;
|
||||||
|
sf::RenderWindow _window;
|
||||||
|
sf::RectangleShape _robotShape;
|
||||||
|
PosTracker _tracker;
|
||||||
|
PosHistory _history;
|
||||||
|
MotorJitterFilter _filter;
|
||||||
|
float _speed_factor;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ControlWindow(Robot& robot, Vector2f robot_size, float wheel_diameter, float latice_count, int history_count, float speed_factor, float filter_delay) :
|
||||||
|
_robot(robot),
|
||||||
|
_window(sf::RenderWindow(
|
||||||
|
sf::VideoMode(800u, 600u),
|
||||||
|
"robot-command",
|
||||||
|
sf::Style::Titlebar | sf::Style::Close
|
||||||
|
)),
|
||||||
|
_robotShape(sf::RectangleShape(robot_size)),
|
||||||
|
_tracker(PosTracker(Vector2f(90, 90), 0., robot_size.y, wheel_diameter, latice_count)),
|
||||||
|
_history(PosHistory(history_count)),
|
||||||
|
_speed_factor(speed_factor),
|
||||||
|
_filter(filter_delay)
|
||||||
|
{
|
||||||
|
_window.setVerticalSyncEnabled(true);
|
||||||
|
_robotShape.setOrigin(sf::Vector2f(robot_size.x / 2, robot_size.y / 2));
|
||||||
|
_robotShape.setFillColor(sf::Color::Blue);
|
||||||
|
_tracker.update(robot.getEncoderWheelValue(1), robot.getEncoderWheelValue(0));
|
||||||
|
_tracker.reset(Vector2f(90, 90), 0.);
|
||||||
|
}
|
||||||
|
|
||||||
|
void run() {
|
||||||
|
while (_window.isOpen()) {
|
||||||
|
update();
|
||||||
|
controller_follow_border_step(_robot, _speed_factor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void update() {
|
||||||
|
_tracker.update(_robot.getEncoderWheelValue(1), _robot.getEncoderWheelValue(0));
|
||||||
|
|
||||||
|
_robotShape.setPosition(_tracker.getPosition());
|
||||||
|
_robotShape.setRotation(_tracker.getRotation());
|
||||||
|
_history.append(_tracker.getPosition());
|
||||||
|
|
||||||
|
event(_window, _robot);
|
||||||
|
_window.clear();
|
||||||
|
_window.draw(_robotShape);
|
||||||
|
_window.draw(_history.vertices());
|
||||||
|
_window.display();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char ** argv)
|
||||||
|
{
|
||||||
|
signal(SIGKILL, stop_handler);
|
||||||
|
std::string hostIpAddress("127.0.0.1");
|
||||||
|
uint16_t tcpPort = 6543;
|
||||||
|
|
||||||
|
if (argc == 3)
|
||||||
|
{
|
||||||
|
hostIpAddress = std::string(argv[1]);
|
||||||
|
std::istringstream iss(argv[2]);
|
||||||
|
iss.exceptions(std::istringstream::failbit);
|
||||||
|
iss >> tcpPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Try to connect to " << hostIpAddress << ":" << tcpPort << " ..." << std::endl;
|
||||||
|
Robot robot(hostIpAddress, tcpPort);
|
||||||
|
robot.waitReady();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// #define STEP_1
|
||||||
|
#ifdef STEP_1
|
||||||
|
controller_follow_border(robot);
|
||||||
|
#else
|
||||||
|
ControlWindow(robot, Vector2f(ROBOT_SIZE, ROBOT_SIZE), WHEEL_DIAMETER, LATICE_COUNT, 4000, 0.78, 1).run();
|
||||||
|
#endif
|
||||||
|
} catch (std::exception &e) {
|
||||||
|
std::cerr << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
137
src/robot.cpp
Normal file
137
src/robot.cpp
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
#include "robot.hpp"
|
||||||
|
|
||||||
|
#include <json/value.h>
|
||||||
|
|
||||||
|
|
||||||
|
Robot::Robot(const std::string & hostIpAddress, uint16_t tcpPort)
|
||||||
|
: _jsonRpcTcpClient(hostIpAddress, tcpPort)
|
||||||
|
, _irProximitysDistanceDetected(this)
|
||||||
|
, _lineTracksIsDetected(this, 1, false)
|
||||||
|
, _lineTracksValue(this, 1, 0)
|
||||||
|
, _encoderWheelsValue(this, 2, 0)
|
||||||
|
, _switchsIsDetected(this)
|
||||||
|
, _ultrasoundsDistanceDetected(this)
|
||||||
|
, _isReadySemaphore(0)
|
||||||
|
, _eventCvMutex()
|
||||||
|
, _eventCv()
|
||||||
|
{
|
||||||
|
_jsonRpcTcpClient.bindNotification("irProximityDistanceDetected", [this](const Json::Value & params){
|
||||||
|
std::size_t index = params["index"].asUInt();
|
||||||
|
_irProximitysDistanceDetected.set(index, params["value"].asUInt(), params["changedCount"].asInt());
|
||||||
|
});
|
||||||
|
_jsonRpcTcpClient.bindNotification("lineTrackIsDetected", [this](const Json::Value & params){
|
||||||
|
std::size_t index = params["index"].asUInt();
|
||||||
|
_lineTracksIsDetected.set(index, params["value"].asBool(), params["changedCount"].asInt());
|
||||||
|
});
|
||||||
|
_jsonRpcTcpClient.bindNotification("lineTrackValue", [this](const Json::Value & params){
|
||||||
|
std::size_t index = params["index"].asUInt();
|
||||||
|
_lineTracksValue.set(index, params["value"].asUInt(), params["changedCount"].asInt());
|
||||||
|
});
|
||||||
|
_jsonRpcTcpClient.bindNotification("encoderWheelValue", [this](const Json::Value & params){
|
||||||
|
std::size_t index = params["index"].asUInt();
|
||||||
|
_encoderWheelsValue.set(index, params["value"].asUInt(), params["changedCount"].asInt());
|
||||||
|
});
|
||||||
|
_jsonRpcTcpClient.bindNotification("switchIsDetected", [this](const Json::Value & params){
|
||||||
|
std::size_t index = params["index"].asUInt();
|
||||||
|
_switchsIsDetected.set(index, params["value"].asBool(), params["changedCount"].asInt());
|
||||||
|
});
|
||||||
|
_jsonRpcTcpClient.bindNotification("ultrasoundDistanceDetected", [this](const Json::Value & params){
|
||||||
|
std::size_t index = params["index"].asUInt();
|
||||||
|
_ultrasoundsDistanceDetected.set(index, params["value"].asUInt(), params["changedCount"].asInt());
|
||||||
|
});
|
||||||
|
_jsonRpcTcpClient.bindNotification("setIsReady", [this](const Json::Value & params){
|
||||||
|
assert(params.isNull());
|
||||||
|
_isReadySemaphore.release();
|
||||||
|
});
|
||||||
|
_jsonRpcTcpClient.startReceive();
|
||||||
|
}
|
||||||
|
|
||||||
|
Robot::~Robot()
|
||||||
|
{}
|
||||||
|
|
||||||
|
void Robot::setMotorPower(MotorIndex motorIndex, float value)
|
||||||
|
{
|
||||||
|
Json::Value params;
|
||||||
|
params["motorIndex"] = motorIndexToStringHelper(motorIndex);
|
||||||
|
params["value"] = value;
|
||||||
|
_jsonRpcTcpClient.callNotification("setMotorPower", params);
|
||||||
|
|
||||||
|
}
|
||||||
|
void Robot::setMotorsPower(float rightValue, float leftValue)
|
||||||
|
{
|
||||||
|
Json::Value params;
|
||||||
|
params["rightValue"] = rightValue;
|
||||||
|
params["leftValue"] = leftValue;
|
||||||
|
_jsonRpcTcpClient.callNotification("setMotorsPower", params);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Robot::waitChanged(EventType eventType) {
|
||||||
|
auto eventTypes = waitParamHelper({eventType});
|
||||||
|
waitChangedHelper(eventTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
EventType Robot::waitChanged(const std::set<EventType> & eventTypes)
|
||||||
|
{
|
||||||
|
auto eventTypesWithChangedCount = waitParamHelper(eventTypes);
|
||||||
|
return waitChangedHelper(eventTypesWithChangedCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Robot::notify(EventType eventType, int changedCount)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lk(_eventCvMutex);
|
||||||
|
_lastNotifiedEventType.insert_or_assign(eventType, changedCount);
|
||||||
|
}
|
||||||
|
_eventCv.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Robot::motorIndexToStringHelper(MotorIndex motorIndex)
|
||||||
|
{
|
||||||
|
switch (motorIndex)
|
||||||
|
{
|
||||||
|
case MotorIndex::RIGHT:
|
||||||
|
return "RIGHT";
|
||||||
|
case MotorIndex::LEFT:
|
||||||
|
return "LEFT";
|
||||||
|
}
|
||||||
|
throw std::invalid_argument(std::string("Cannot convert ")
|
||||||
|
+ std::to_string(static_cast<int>(motorIndex)) + " into Robot::MotorIndex");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Robot::waitChangedHelper(EventType eventType, int & changedCount) {
|
||||||
|
std::map<EventType, int> eventTypes{{eventType, changedCount}};
|
||||||
|
waitChangedHelper(eventTypes);
|
||||||
|
changedCount = eventTypes.at(eventType);
|
||||||
|
}
|
||||||
|
|
||||||
|
EventType Robot::waitChangedHelper(std::map<EventType, int> & eventTypes) {
|
||||||
|
std::unique_lock<std::mutex> lk(_eventCvMutex);
|
||||||
|
EventType notifiedEventType;
|
||||||
|
_eventCv.wait(lk, [eventTypes, ¬ifiedEventType, lastNotifiedEventType = std::ref(_lastNotifiedEventType)]{
|
||||||
|
for (auto eventType : eventTypes) {
|
||||||
|
if (lastNotifiedEventType.get().at(eventType.first)>eventType.second)
|
||||||
|
{
|
||||||
|
lastNotifiedEventType.get().at(eventType.first) = eventType.second;
|
||||||
|
notifiedEventType = eventType.first;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
return notifiedEventType;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<EventType, int> Robot::waitParamHelper(std::set<EventType> eventTypes)
|
||||||
|
{
|
||||||
|
std::map<EventType, int> toReturn;
|
||||||
|
std::unique_lock<std::mutex> lk(_eventCvMutex);
|
||||||
|
for (auto eventType : eventTypes)
|
||||||
|
{
|
||||||
|
int changedCount = -1;
|
||||||
|
auto it = _lastNotifiedEventType.find(eventType);
|
||||||
|
if (it != _lastNotifiedEventType.end())
|
||||||
|
changedCount = it->second;
|
||||||
|
toReturn.insert(std::make_pair(eventType, changedCount));
|
||||||
|
}
|
||||||
|
return toReturn;
|
||||||
|
}
|
158
src/robot.hpp
Normal file
158
src/robot.hpp
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
#ifndef ROBOT_HPP
|
||||||
|
#define ROBOT_HPP
|
||||||
|
|
||||||
|
#include "jsonrpctcpclient.hpp"
|
||||||
|
#include "values.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <optional>
|
||||||
|
#include <semaphore>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <set>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
enum class EventType
|
||||||
|
{
|
||||||
|
IR_PROXIMITYS_DISTANCE_DETECTED, //!< A new value of IR distance have been received
|
||||||
|
LINE_TRACKS_IS_DETECTED, //!< A new value of the boolean is line detected have been received
|
||||||
|
LINE_TRACKS_VALUE, //!< A new value of the raw line color have been received
|
||||||
|
ENCODER_WHEELS_VALUE, //!< A new value of the encoder wheels have been received
|
||||||
|
SWITCHS_IS_DETECTED, //!< A new value of the switch have been received
|
||||||
|
ULTRASOUNDS_DISTANCE_DETECTED //!< A new value of ultrasound distance have been received
|
||||||
|
};
|
||||||
|
|
||||||
|
class Robot : public IRobot<EventType>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//! @brief Create a new robot connexion with a robot server (simu or reel).
|
||||||
|
Robot(const std::string & hostIpAddress, uint16_t tcpPort);
|
||||||
|
|
||||||
|
//! @brief Close the robot connexion.
|
||||||
|
virtual ~Robot();
|
||||||
|
|
||||||
|
//! @brief Wait for the robot server to be ready to send or receive messages.
|
||||||
|
inline void waitReady() {using namespace std::chrono_literals; _isReadySemaphore.acquire();
|
||||||
|
std::this_thread::sleep_for(100ms);}
|
||||||
|
|
||||||
|
//! @return The last IR distance receive from robot server (in pixel on simu).
|
||||||
|
//! @param index The index of this sensor ont he robot starting from 0.
|
||||||
|
std::size_t getIrProximitysDistanceDetected(std::size_t index) const {return _irProximitysDistanceDetected.get(index);}
|
||||||
|
|
||||||
|
//! @return True if the line has been detected in the last receive message from robot server.
|
||||||
|
//! @param index The index of this sensor ont he robot starting from 0.
|
||||||
|
bool getLineTracksIsDetected(std::size_t index) const {return _lineTracksIsDetected.get(index);}
|
||||||
|
|
||||||
|
//! @return The last raw line color receive from robot server (0 to 255).
|
||||||
|
//! @param index The index of this sensor ont he robot starting from 0.
|
||||||
|
std::uint8_t getLineTracksValue(std::size_t index) const {return _lineTracksValue.get(index);}
|
||||||
|
|
||||||
|
//! @return The last encoder wheel latice count receive from robot server.
|
||||||
|
//! @param index The index of this sensor ont he robot starting from 0.
|
||||||
|
std::size_t getEncoderWheelValue(std::size_t index) const {return _encoderWheelsValue.get(index);}
|
||||||
|
|
||||||
|
//! @return True if last switch state receive from robot server is pressed.
|
||||||
|
//! @param index The index of this sensor ont he robot starting from 0.
|
||||||
|
bool getSwitchsIsDetected(std::size_t index) const {return _switchsIsDetected.get(index);}
|
||||||
|
|
||||||
|
//! @return The last ultrasound distance receive from robot server (in pixel on simu).
|
||||||
|
//! @param index The index of this sensor ont he robot starting from 0.
|
||||||
|
std::size_t getUltrasoundsDistanceDetected(std::size_t index) const {return _ultrasoundsDistanceDetected.get(index);}
|
||||||
|
|
||||||
|
//! @brief Set current motor(s) power(s).
|
||||||
|
//! @param value PWM between -1.0 and 1.0.
|
||||||
|
//! \{
|
||||||
|
enum class MotorIndex {RIGHT = 0, LEFT = 1};
|
||||||
|
void setMotorPower(MotorIndex motorIndex, float value);
|
||||||
|
void setMotorsPower(float rightValue, float leftValue);
|
||||||
|
//! \}
|
||||||
|
|
||||||
|
//! @brief Wait until this specific event has been received
|
||||||
|
void waitChanged(EventType eventType);
|
||||||
|
|
||||||
|
//! @brief Wait until one of the listed event has been received
|
||||||
|
//! @return The received event
|
||||||
|
EventType waitChanged(const std::set<EventType> & eventType);
|
||||||
|
|
||||||
|
//! @brief Wait until this specific event has been received or timeout
|
||||||
|
//! @return True if this event has been receive or false if timeout
|
||||||
|
template<typename _Rep, typename _Period>
|
||||||
|
bool waitChanged(EventType eventType, const std::chrono::duration<_Rep, _Period> & duration);
|
||||||
|
|
||||||
|
//! @brief Wait until one of the listed event has been received
|
||||||
|
//! @return The received event if a event of the list has been received or an empty value if timeout
|
||||||
|
template<typename _Rep, typename _Period>
|
||||||
|
std::optional<EventType> waitChanged(const std::set<EventType> & eventTypes, const std::chrono::duration<_Rep, _Period> & duration);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Robot(const Robot &) = delete;
|
||||||
|
Robot & operator=(const Robot &) = delete;
|
||||||
|
|
||||||
|
void notify(EventType eventType, int changedCount) override;
|
||||||
|
|
||||||
|
static std::string motorIndexToStringHelper(MotorIndex motorIndex);
|
||||||
|
void waitChangedHelper(EventType eventType, int & changedCount);
|
||||||
|
EventType waitChangedHelper(std::map<EventType, int> & eventTypes);
|
||||||
|
template<typename _Rep, typename _Period>
|
||||||
|
bool waitChangedHelper(EventType eventType, int & changedCount, const std::chrono::duration<_Rep, _Period> & duration);
|
||||||
|
template<typename _Rep, typename _Period>
|
||||||
|
std::optional<EventType> waitChangedHelper(std::map<EventType, int> & eventTypes, const std::chrono::duration<_Rep, _Period> & duration);
|
||||||
|
std::map<EventType, int> waitParamHelper(std::set<EventType> eventTypes);
|
||||||
|
|
||||||
|
JsonRpcTcpClient _jsonRpcTcpClient;
|
||||||
|
Values<std::size_t, EventType, EventType::IR_PROXIMITYS_DISTANCE_DETECTED> _irProximitysDistanceDetected;
|
||||||
|
Values<bool, EventType, EventType::LINE_TRACKS_IS_DETECTED> _lineTracksIsDetected;
|
||||||
|
Values<std::uint8_t, EventType, EventType::LINE_TRACKS_VALUE> _lineTracksValue;
|
||||||
|
Values<std::size_t, EventType, EventType::ENCODER_WHEELS_VALUE> _encoderWheelsValue;
|
||||||
|
Values<bool, EventType, EventType::SWITCHS_IS_DETECTED> _switchsIsDetected;
|
||||||
|
Values<std::size_t, EventType, EventType::ULTRASOUNDS_DISTANCE_DETECTED> _ultrasoundsDistanceDetected;
|
||||||
|
std::binary_semaphore _isReadySemaphore;
|
||||||
|
std::mutex _eventCvMutex;
|
||||||
|
std::condition_variable _eventCv;
|
||||||
|
std::map<EventType, int> _lastNotifiedEventType;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template<typename _Rep, typename _Period>
|
||||||
|
bool Robot::waitChanged(EventType eventType, const std::chrono::duration<_Rep, _Period> & duration)
|
||||||
|
{
|
||||||
|
auto eventTypes = waitParamHelper({eventType});
|
||||||
|
return waitChangedHelper(eventTypes, duration).has_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename _Rep, typename _Period>
|
||||||
|
bool Robot::waitChangedHelper(EventType eventType, int & changedCount, const std::chrono::duration<_Rep, _Period> & duration)
|
||||||
|
{
|
||||||
|
std::map<EventType, int> eventTypes{{eventType, changedCount}};
|
||||||
|
bool toReturn = waitChangedHelper(eventTypes, duration).has_value();
|
||||||
|
changedCount = eventTypes.at(eventType);
|
||||||
|
return toReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename _Rep, typename _Period>
|
||||||
|
std::optional<EventType> Robot::waitChanged(const std::set<EventType> & eventTypes, const std::chrono::duration<_Rep, _Period> & duration)
|
||||||
|
{
|
||||||
|
auto eventTypesWithChangedCount = waitParamHelper(eventTypes);
|
||||||
|
return waitChangedHelper(eventTypesWithChangedCount, duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename _Rep, typename _Period>
|
||||||
|
std::optional<EventType> Robot::waitChangedHelper(std::map<EventType, int> & eventTypes, const std::chrono::duration<_Rep, _Period> & duration)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lk(_eventCvMutex);
|
||||||
|
EventType notifiedEventType;
|
||||||
|
if (!_eventCv.wait_for(lk, duration, [eventTypes, ¬ifiedEventType, lastNotifiedEventType = std::ref(_lastNotifiedEventType)]{
|
||||||
|
for (auto eventType : eventTypes) {
|
||||||
|
if (lastNotifiedEventType.get().at(eventType.first)>eventType.second)
|
||||||
|
{
|
||||||
|
lastNotifiedEventType.get().at(eventType.first) = eventType.second;
|
||||||
|
notifiedEventType = eventType.first;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}))
|
||||||
|
return {};
|
||||||
|
return notifiedEventType;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
5
src/utils.hpp
Normal file
5
src/utils.hpp
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#define DBG(X) ({ auto value = X; std::cout << "(DBG) [" << __FILE__ << ":" << __LINE__ << "] (" << #X << ") = " << value << "\n"; value;})
|
63
src/values.hpp
Normal file
63
src/values.hpp
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
#ifndef VALUES_HPP
|
||||||
|
#define VALUES_HPP
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <mutex>
|
||||||
|
#include <optional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
template<typename EventType>
|
||||||
|
class IRobot
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~IRobot() {}
|
||||||
|
virtual void notify(EventType eventType, int changedCount) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template<typename T, typename EventType, EventType EVENT_TYPE_VALUE>
|
||||||
|
class Values
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Values(IRobot<EventType> * robot) : _robot(robot) {}
|
||||||
|
Values(IRobot<EventType> * robot, size_t values_count, T defaults) : _robot(robot) {
|
||||||
|
for (size_t i = 0; i < values_count; i += 1) {
|
||||||
|
auto value = Value();
|
||||||
|
value._value = defaults;
|
||||||
|
this->_values.push_back(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline T get(std::size_t index) const {const std::lock_guard<std::mutex> lock(_mutex); return _values.at(index)._value.value();}
|
||||||
|
inline int getChangedCount(std::size_t index) const {const std::lock_guard<std::mutex> lock(_mutex); return _values.at(index)._changedCount.value();}
|
||||||
|
void set(std::size_t index, T v, int changedCount)
|
||||||
|
{
|
||||||
|
const std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
if (index>=_values.size())
|
||||||
|
_values.resize(index+1);
|
||||||
|
Value & value = _values.at(index);
|
||||||
|
value._value = v;
|
||||||
|
if (value._changedCount.has_value())
|
||||||
|
{
|
||||||
|
value._changedCount.value()++;
|
||||||
|
assert(value._changedCount == changedCount);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
value._changedCount = changedCount;
|
||||||
|
_robot->notify(EVENT_TYPE_VALUE, changedCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Value
|
||||||
|
{
|
||||||
|
Value() : _value(), _changedCount() {}
|
||||||
|
std::optional<T> _value;
|
||||||
|
std::optional<int> _changedCount;
|
||||||
|
};
|
||||||
|
std::vector<Value> _values;
|
||||||
|
mutable std::mutex _mutex;
|
||||||
|
IRobot<EventType> * _robot;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
5
watch.sh
Executable file
5
watch.sh
Executable file
|
@ -0,0 +1,5 @@
|
||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
cd "$(dirname "$(realpath "$0")")"
|
||||||
|
|
||||||
|
nodemon -w src -e c,cpp,h,hpp -x './build.sh && ./run.sh'
|
Loading…
Add table
Add a link
Reference in a new issue