MyoLinux
bled112client.h
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 #pragma once
6 #ifndef MYOLINUX_BLED112CLIENT_H
7 #define MYOLINUX_BLED112CLIENT_H
8 
9 #include "myolinux.h"
10 #include "serial.h"
11 #include "firstargument.h"
12 #include "bleapi.h"
13 
14 #include <functional>
15 #include <map>
16 #include <iostream>
17 
18 namespace MYOLINUX_NAMESPACE {
19 
21 namespace bled112 {
22 
24 class Client {
25 public:
26  Client(const Serial &socket)
27  : socket(socket)
28  { }
29 
30  template <typename T>
31  void write(const T &);
32 
33  template <typename T>
34  void write(const T &, const Buffer &);
35 
36  template <typename T>
37  T read();
38 
39  template <typename T>
40  T read(Buffer &);
41 
42  template <typename... Functions>
43  void read(const Functions&...);
44 
45 private:
46  template <typename Function>
47  using DisableIfFirstArgumentIsPartial = std::enable_if<!Partial<typename FirstArgument<Function>::type>::value>;
48 
49  template <typename Function>
50  using EnableIfFirstArgumentIsPartial = std::enable_if<Partial<typename FirstArgument<Function>::type>::value>;
51 
52  Header readHeader();
53 
54  template <typename T>
55  void checkHeader(const Header &);
56 
57  template <typename T>
58  T readPayload(const Header &);
59 
60  template <typename T>
61  T readPayload(const Header &, Buffer &);
62 
63  void dispatch(const Header &);
64 
65  template <typename Function, typename... Functions>
66  auto dispatch(const Header &, const Function &, const Functions&...)
67  -> typename DisableIfFirstArgumentIsPartial<Function>::type;
68 
69  template <typename Function, typename... Functions>
70  auto dispatch(const Header &, const Function &, const Functions&...)
71  -> typename EnableIfFirstArgumentIsPartial<Function>::type;
72 
73  Serial socket;
74 };
75 
78 template <typename T>
79 void Client::write(const T &payload)
80 {
81  socket.write(pack(getHeader<T>()));
82  socket.write(pack(payload));
83 }
84 
88 template <typename T>
89 void Client::write(const T &payload, const Buffer &leftover)
90 {
91  socket.write(pack(getHeader<T>(leftover.size())));
92  socket.write(pack(payload));
93  socket.write(leftover);
94 }
95 
96 inline Header Client::readHeader()
97 {
98  return unpack<Header>(socket.read(sizeof(Header)));
99 }
100 
101 template <typename T>
102 void Client::checkHeader(const Header &header)
103 {
104  if (header.cls != T::cls) {
105  throw std::runtime_error("Class index does not match the expected value.");
106  }
107  if (header.cmd != T::cmd) {
108  throw std::runtime_error("Command index does not match the expected value.");
109  }
110  if (!Partial<T>::value && header.length() != sizeof(T)) {
111  throw std::runtime_error("Payload size does not match the expected value.");
112  }
113 }
114 
115 template <typename T>
116 T Client::readPayload(const Header &header)
117 {
118  return unpack<T>(socket.read(header.length()));
119 }
120 
121 template <typename T>
122 T Client::readPayload(const Header &header, Buffer &leftover)
123 {
124  const auto payload = unpack<T>(socket.read(sizeof(T)));
125  leftover = socket.read(header.length() - sizeof(T));
126  return payload;
127 }
128 
131 template <typename T>
132 T Client::read()
133 {
134  const auto header = readHeader();
135  checkHeader<T>(header);
136  return readPayload<T>(header);
137 }
138 
142 template <typename T>
143 T Client::read(Buffer &leftover)
144 {
145  const auto header = readHeader();
146  checkHeader<T>(header);
147  return readPayload<T>(header, leftover);
148 }
149 
150 inline void Client::dispatch(const Header &)
151 { }
152 
153 template <typename Function, typename... Functions>
154 auto Client::dispatch(const Header &header, const Function &function, const Functions&... functions)
155  -> typename DisableIfFirstArgumentIsPartial<Function>::type
156 {
157  using T = typename FirstArgument<Function>::type;
158 
159  if (header.cls == T::cls && header.cmd == T::cmd && header.length() == sizeof(T)) {
160  function(readPayload<T>(header));
161  return;
162  }
163  dispatch(header, functions...);
164 }
165 
166 template <typename Function, typename... Functions>
167 auto Client::dispatch(const Header &header, const Function &function, const Functions&... functions)
168  -> typename EnableIfFirstArgumentIsPartial<Function>::type
169 {
170  using T = typename FirstArgument<Function>::type;
171 
172  if (header.cls == T::cls && header.cmd == T::cmd) {
173  Buffer leftover;
174  const auto payload = readPayload<T>(header, leftover);
175 
176  function(std::move(payload), std::move(leftover));
177  return;
178  }
179  dispatch(header, functions...);
180 }
181 
191 template <typename... Functions>
192 void Client::read(const Functions&... functions)
193 {
194  const auto header = readHeader();
195  dispatch(header, functions...);
196 }
197 
198 }
199 }
200 
201 #endif // MYOLINUX_BLED112CLIENT_H
Class for communication using the BlueGiga protocol.
Definition: bled112client.h:24
std::vector< unsigned char > Buffer
Buffer used for packing and unpacking packets.
Definition: buffer.h:16
Buffer pack(const T &payload)
Pack payload.
Definition: buffer.h:20
std::size_t write(const Buffer &)
Read from serial port.
Definition: serial.cpp:86
Class for communication over the serial port.
Definition: serial.h:19