Skip to content

Commit

Permalink
Merge pull request #1 from dhollman/async
Browse files Browse the repository at this point in the history
Async impl example
  • Loading branch information
griswaldbrooks authored Oct 5, 2023
2 parents a6e3219 + 2391952 commit af5148b
Showing 1 changed file with 64 additions and 60 deletions.
124 changes: 64 additions & 60 deletions src/main.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#include <iostream>
#include <future>
#include <memory>
#include <stdexcept>
#include <string>
#include "serial/serial.h"
#include "mdspan.hpp"
#include "expected.hpp"

namespace stdex = std::experimental;

Expand All @@ -18,82 +20,84 @@ struct robot_arm {
serial::Timeout timeout = serial::Timeout::simpleTimeout(10000))
: serial_(std::move(port), baudrate, std::move(timeout)) {}

tl::expected<bin_state, std::string> is_bin_occupied(int bin) const {
bin_state is_bin_occupied(int bin) {
if (!serial_.isOpen()) {
return tl::make_unexpected("Error opening serial port");
throw std::runtime_error("Error opening serial port");
}
serial_.write(std::to_string(bin));
try {
auto const result = std::stoi(serial_.readline());
if (result == 1) {
return bin_state::OCCUPIED;
}
return bin_state::EMPTY;
} catch (std::invalid_argument const& e) {
return tl::make_unexpected("Invalid argument from serial port");
} catch (std::out_of_range const& e) {
return tl::make_unexpected("Out of range from serial port");
}
auto const result = std::stoi(serial_.readline());
if (result == 1) {
return bin_state::OCCUPIED;
}
return bin_state::EMPTY;
}

mutable serial::Serial serial_;
serial::Serial serial_;
};


template <int nbins>
struct bin_checker {
using element_type = tl::expected<bin_state, std::string>;
using reference = tl::expected<bin_state, std::string> const&;
using data_handle_type = robot_arm*;

inline static element_type const no_bin = tl::make_unexpected("Invalid bin index");
mutable element_type recent_ = tl::make_unexpected("Bin has not been accessed");

reference access(data_handle_type arm, std::ptrdiff_t offset) const {
if (offset < 0 || offset >= nbins) {
return no_bin;
struct bounds_checked_layout_policy {
template <class Extents>
struct mapping : stdex::layout_right::mapping<Extents> {
using base_t = stdex::layout_right::mapping<Extents>;
using base_t::base_t;
std::ptrdiff_t operator()(auto... idxs) const {
[&]<size_t... Is>(std::index_sequence<Is...>) {
if (((idxs < 0 || idxs > this->extents().extent(Is)) || ...)) {
throw std::out_of_range("Invalid bin index");
}
}(std::make_index_sequence<sizeof...(idxs)>{});
return this->base_t::operator()(idxs...);
}
try {
recent_ = arm->is_bin_occupied(static_cast<int>(offset));
} catch (std::exception const& e) {
recent_ = tl::make_unexpected(std::string{"Error opening serial port"});
}
return recent_;
}
};
};

using ext_t = stdex::extents<uint32_t, 2, 3>;
using acc_t = bin_checker<6>;
using bin_view = stdex::mdspan<bin_state, ext_t, stdex::layout_right, acc_t>;
struct robot_command_accessor {
using element_type = bin_state;
using reference = std::future<bin_state>;
using data_handle_type = robot_arm*;

auto print_state = [](bin_state const& state) -> tl::expected<void, std::string> {
switch (state) {
case bin_state::OCCUPIED:
std::cout << "OCCUPIED";
break;
case bin_state::EMPTY:
std::cout << "EMPTY";
break;
reference access(data_handle_type ptr, std::ptrdiff_t offset) const {
// We know ptr will be valid asynchronously because we construct it on the stack
// of main. In real code we might want to use a shared_ptr or something
return std::async([=]{
return ptr->is_bin_occupied(static_cast<int>(offset));
});
}
return {};
};

auto shrug = [](std::string const& msg) -> void {
std::cout << "¯\\_(ツ)_/¯ " << msg;
};
using bin_view = stdex::mdspan<bin_state,
// We're treating our 6 bins as a 3x2 matrix
stdex::extents<uint32_t, 3, 2>,
// Our layout should do bounds-checking
bounds_checked_layout_policy,
// Our accessor should tell the robot to asynchronously access the bin
robot_command_accessor
>;

int main(int, char **) {
int main() {
auto arm = robot_arm{"/dev/ttyACM0", 9600};
auto bins = bin_view(&arm, {}, bin_checker<6>{});
auto bins = bin_view(&arm);
while(true) {
for (std::size_t i = 0; i != bins.extent(0); ++i) {
for (std::size_t j = 0; j != bins.extent(1); ++j) {
std::cout << "Bin " << i << ", " << j << " is ";
bins(i, j).and_then(print_state).or_else(shrug);
std::cout << "\n";
for (auto i = 0u; i < bins.extent(0); ++i) {
for (auto j = 0u; j < bins.extent(1); ++j) {
std::cout << "Bin " << i << ", " << j << " is ";
try {
auto state = bins(i, j).get();
switch (state) {
case bin_state::OCCUPIED:
std::cout << "OCCUPIED";
break;
case bin_state::EMPTY:
std::cout << "EMPTY";
break;
}
} catch (std::exception const& e) {
std::cout << "¯\\_(ツ)_/¯ " << e.what();
}
std::cout << "\n";
}
}
}
std::cout << "====================\n";
std::cout << "====================\n";
}
return 0;
}
}

0 comments on commit af5148b

Please sign in to comment.