2#include <ossia/network/context.hpp>
3#include <ossia/network/sockets/can_socket.hpp>
6#include <ossia-qt/protocols/utils.hpp>
8#include <boost/asio/dispatch.hpp>
16#include <nano_observer.hpp>
55 ,
public Nano::Observer
57 W_OBJECT(qml_can_socket)
61 ossia::net::can_socket socket;
62 std::atomic_bool alive{
true};
64 state(
const ossia::net::can_configuration& conf, boost::asio::io_context& ctx)
71 struct receive_callback
73 std::shared_ptr<state> st;
74 QPointer<qml_can_socket> self;
76 void operator()(
const ossia::net::can_message& msg)
const
82 frame[
"id"] = QVariant::fromValue(msg.id);
83 frame[
"extended"] = msg.extended;
84 frame[
"rtr"] = msg.remote;
86 frame[
"error"] = msg.error;
87 frame[
"brs"] = msg.bitrate_switch;
88 frame[
"esi"] = msg.error_state;
93 frame[
"bytes"] = msg.remote ? QByteArray{}
95 reinterpret_cast<const char*
>(msg.data),
97 frame[
"length"] = int(msg.size);
99 ossia::qt::run_async(self.get(), [self = self, frame] {
102 if(self->onMessage.isCallable())
103 if(auto engine = qjsEngine(self.get()))
104 self->onMessage.call({engine->toScriptValue(frame)});
105 }, Qt::AutoConnection);
115 m_state->alive =
false;
120 bool isOpen() const noexcept {
return m_state !=
nullptr; }
122 void open(
const ossia::net::can_configuration& conf, boost::asio::io_context& ctx)
124 m_state = std::make_shared<state>(conf, ctx);
126 auto& sock = m_state->socket;
127 sock.on_open.connect<&qml_can_socket::on_open>(
this);
128 sock.on_close.connect<&qml_can_socket::on_close>(
this);
129 sock.on_fail.connect<&qml_can_socket::on_fail>(
this);
130 sock.on_write_error.connect<&qml_can_socket::on_write_error>(
this);
140 m_state->alive =
false;
147 void write(QVariant frame)
152 const auto map = frame.toMap();
153 ossia::net::can_message msg;
154 msg.id = map[
"id"].toUInt();
155 msg.extended = map[
"extended"].toBool();
156 msg.remote = map[
"rtr"].toBool();
157 msg.fd = map[
"fd"].toBool();
158 msg.bitrate_switch = map[
"brs"].toBool();
160 const auto max = msg.fd ? CANFD_MAX_DLEN : CAN_MAX_DLEN;
162 const auto bytes = map[
"bytes"];
165 if(bytes.canConvert<QVariantList>() && bytes.typeId() != QMetaType::QByteArray)
167 for(
const auto& b : bytes.toList())
171 msg.data[n++] = uint8_t(b.toUInt() & 0xFF);
176 const auto arr = bytes.toByteArray();
181 msg.data[n++] = uint8_t(b);
187 if(
auto len = map[
"length"]; len.isValid())
188 msg.size = uint8_t(std::min(len.toInt(),
max));
190 msg.size = uint8_t(n);
193 boost::asio::dispatch(context(), [st, msg] {
195 st->socket.write(msg);
205 boost::asio::dispatch(context(), [st] { st->socket.close(); });
211 if(!m_state || !m_state->alive)
214 auto self = QPointer{
this};
215 if(onMessage.isCallable())
216 m_state->socket.receive(receive_callback{m_state, self});
218 if(onOpen.isCallable())
219 ossia::qt::run_async(
this, [=,
this] {
220 onOpen.call({qjsEngine(
this)->newQObject(
this)});
221 }, Qt::AutoConnection);
226 if(!m_state || !m_state->alive)
228 if(onError.isCallable())
229 ossia::qt::run_async(
this, [=,
this] {
230 onError.call({QStringLiteral(
"CAN socket failed")});
231 }, Qt::AutoConnection);
238 void on_write_error(boost::system::error_code ec)
240 if(!m_state || !m_state->alive)
242 if(!onError.isCallable())
245 const auto err = QString::fromStdString(ec.message());
246 ossia::qt::run_async(
this, [self = QPointer{
this}, err] {
247 if(self && self->onError.isCallable())
248 self->onError.call({QStringLiteral(
"write: ") + err});
249 }, Qt::AutoConnection);
254 if(!m_state || !m_state->alive)
256 if(onClose.isCallable())
257 ossia::qt::run_async(
this, [=,
this] { onClose.call(); }, Qt::AutoConnection);
266 boost::asio::io_context& context() const noexcept {
return m_state->socket.m_context; }
268 std::shared_ptr<state> m_state;
Definition qml_device.cpp:43
OSSIA_INLINE constexpr auto max(const T a, const U b) noexcept -> typename std::conditional<(sizeof(T) > sizeof(U)), T, U >::type
max function tailored for values
Definition math.hpp:96