Direct-BT v3.3.0-1-gc2d430c
Direct-BT - Direct Bluetooth Programming.
BTGattCmd.hpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2021 Gothel Software e.K.
4 * Copyright (c) 2021 ZAFENA AB
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 */
25#include <string>
26#include <mutex>
27#include <condition_variable>
28
29#include <jau/uuid.hpp>
30#include <jau/octets.hpp>
31#include <jau/fraction_type.hpp>
32#include <jau/functional.hpp>
33
34#include "BTGattDesc.hpp"
35#include "BTGattChar.hpp"
36#include "BTGattService.hpp"
37#include "HCITypes.hpp"
38
39#ifndef BT_GATT_CMD_HPP_
40#define BT_GATT_CMD_HPP_
41
42namespace direct_bt {
43
44 /** \addtogroup DBTUserClientAPI
45 *
46 * @{
47 */
48
49 /**
50 * Class maps a GATT command and optionally its asynchronous response
51 * to a synchronous atomic operation.
52 *
53 * The GATT command is issued by writing the associated GATT characteristic value
54 * via BTGattChar::writeValueNoResp() or BTGattChar::writeValue().
55 *
56 * Its optional asynchronous characteristic value notification or indication response
57 * is awaited and collected after command issuance.
58 *
59 * If a response jau::uuid_t is given, notification or indication will be enabled at first send() command
60 * and disabled at close() or destruction.
61 *
62 * @see BTGattChar::writeValueNoResp()
63 * @see BTGattChar::writeValue()
64 * @since 2.4.0
65 */
67 {
68 public:
69 typedef jau::function<void(BTGattCharRef charDecl, const jau::TROOctets& char_value, const uint64_t timestamp)> DataCallback;
70
71 private:
72 /** Name, representing the command */
73 std::string name;
74 /** Command's BTGattService jau::uuid_t, may be nullptr. */
75 const jau::uuid_t* service_uuid;
76 /** Command's BTGattChar value jau::uuid_t to write command, never nullptr. */
77 const jau::uuid_t* cmd_uuid;
78 /** Command's optional BTGattChar value jau::uuid_t for the notification or indication response, may be nullptr. */
79 const jau::uuid_t* rsp_uuid;
80
81 std::mutex mtxCommand;
82 std::mutex mtxRspReceived;
83 std::condition_variable cvRspReceived;
84 BTDevice& dev;
85 jau::POctets rsp_data;
86 BTGattCharRef cmdCharRef;
87 BTGattCharRef rspCharRef;
88 jau::nsize_t rspMinSize;
89 DataCallback dataCallback;
90 bool setup_done;
91
92 class ResponseCharListener : public BTGattCharListener {
93 private:
94 BTGattCmd& source;
95 jau::POctets& rsp_data;
96 void store(const jau::TROOctets& char_value);
97
98 public:
99 ResponseCharListener(BTGattCmd& source_, jau::POctets& rsp_data_)
100 : source(source_), rsp_data(rsp_data_)
101 { }
102
103 void notificationReceived(BTGattCharRef charDecl,
104 const jau::TROOctets& char_value, const uint64_t timestamp) override;
105
106 void indicationReceived(BTGattCharRef charDecl,
107 const jau::TROOctets& char_value, const uint64_t timestamp,
108 const bool confirmationSent) override;
109 };
110 std::shared_ptr<ResponseCharListener> rspCharListener;
111 bool verbose;
112
113 std::string rspCharStr() const noexcept {
114 return nullptr != rspCharRef ? rspCharRef->toString() : "n/a";
115 }
116 std::string srvUUIDStr() const noexcept {
117 return nullptr != service_uuid ? service_uuid->toString() : "n/a";
118 }
119 std::string rspUUIDStr() const noexcept {
120 return nullptr != rsp_uuid ? rsp_uuid->toString() : "n/a";
121 }
122 bool isConnected() const noexcept;
123
124 bool isResolvedEq() const noexcept {
125 return nullptr != cmdCharRef && cmdCharRef->isValidInstance();
126 }
127
128 HCIStatusCode setup() noexcept;
129
130 HCIStatusCode sendImpl(const bool prefNoAck, const jau::TROOctets& cmd_data, const jau::fraction_i64& timeout, bool allowResponse) noexcept;
131
132 public:
133
134 /**
135 * Constructor for commands with notification or indication response.
136 *
137 * @param dev_ the remote BTDevice
138 * @param name_ user given name, representing the command
139 * @param service_uuid_ command's BTGattService jau::uuid_t
140 * @param cmd_uuid_ command's BTGattChar value jau::uuid_t to write the command
141 * @param rsp_uuid_ command's BTGattChar value jau::uuid_t for the notification or indication response.
142 * @param rsp_capacity initial capacity of response sink, see getResponse()
143 */
144 BTGattCmd(BTDevice& dev_, std::string name_,
145 const jau::uuid_t& service_uuid_,
146 const jau::uuid_t& cmd_uuid_,
147 const jau::uuid_t& rsp_uuid_,
148 const jau::nsize_t rsp_capacity) noexcept
149 : name(std::move(name_)),
150 service_uuid(&service_uuid_),
151 cmd_uuid(&cmd_uuid_),
152 rsp_uuid(&rsp_uuid_),
153 dev(dev_),
154 rsp_data(rsp_capacity, 0 /* size */, jau::lb_endian_t::little),
155 cmdCharRef(nullptr),
156 rspCharRef(nullptr),
157 rspMinSize(0),
158 dataCallback(nullptr),
159 setup_done(false),
160 rspCharListener( std::make_shared<ResponseCharListener>( *this, rsp_data) ),
161 verbose(jau::environment::get().debug)
162 { }
163
164 /**
165 * Constructor for commands with notification or indication response.
166 *
167 * Since no service UUID is given, the BTGattChar lookup is less efficient.
168 *
169 * @param dev_ the remote BTDevice
170 * @param name_ user given name, representing the command
171 * @param cmd_uuid_ command's BTGattChar value jau::uuid_t to write the command
172 * @param rsp_uuid_ command's BTGattChar value jau::uuid_t for the notification or indication response.
173 * @param rsp_capacity initial capacity of response sink, see getResponse()
174 */
175 BTGattCmd(BTDevice& dev_, std::string name_,
176 const jau::uuid_t& cmd_uuid_,
177 const jau::uuid_t& rsp_uuid_,
178 const jau::nsize_t rsp_capacity) noexcept
179 : name(std::move(name_)),
180 service_uuid(nullptr),
181 cmd_uuid(&cmd_uuid_),
182 rsp_uuid(&rsp_uuid_),
183 dev(dev_),
184 rsp_data(rsp_capacity, 0 /* size */, jau::lb_endian_t::little),
185 cmdCharRef(nullptr),
186 rspCharRef(nullptr),
187 rspMinSize(0),
188 dataCallback(nullptr),
189 setup_done(false),
190 rspCharListener( std::make_shared<ResponseCharListener>( *this, rsp_data ) ),
191 verbose(jau::environment::get().debug)
192 { }
193
194 /**
195 * Constructor for commands without response.
196 *
197 * @param dev_ the remote BTDevice
198 * @param name_ user given name, representing the command
199 * @param service_uuid_ command's BTGattService jau::uuid_t
200 * @param cmd_uuid_ command's BTGattChar value jau::uuid_t to write the command
201 */
202 BTGattCmd(BTDevice& dev_, std::string name_,
203 const jau::uuid_t& service_uuid_,
204 const jau::uuid_t& cmd_uuid_) noexcept
205 : name(std::move(name_)),
206 service_uuid(&service_uuid_),
207 cmd_uuid(&cmd_uuid_),
208 rsp_uuid(nullptr),
209 dev(dev_),
210 rsp_data(jau::lb_endian_t::little),
211 cmdCharRef(nullptr),
212 rspCharRef(nullptr),
213 rspMinSize(0),
214 dataCallback(nullptr),
215 setup_done(false),
216 rspCharListener( nullptr ),
217 verbose(jau::environment::get().debug)
218 { }
219
220 /**
221 * Constructor for commands without response.
222 *
223 * Since no service UUID is given, the BTGattChar lookup is less efficient.
224 *
225 * @param dev_ the remote BTDevice
226 * @param name_ user given name, representing the command
227 * @param cmd_uuid_ command's BTGattChar value jau::uuid_t to write the command
228 */
229 BTGattCmd(BTDevice& dev_, std::string name_,
230 const jau::uuid_t& cmd_uuid_) noexcept
231 : name(std::move(name_)),
232 service_uuid(nullptr),
233 cmd_uuid(&cmd_uuid_),
234 rsp_uuid(nullptr),
235 dev(dev_),
236 rsp_data(jau::lb_endian_t::little),
237 cmdCharRef(nullptr),
238 rspCharRef(nullptr),
239 rspMinSize(0),
240 dataCallback(nullptr),
241 setup_done(false),
242 rspCharListener( nullptr ),
243 verbose(jau::environment::get().debug)
244 { }
245
246 /**
247 * Close this command instance, usually called at destruction.
248 *
249 * If a response jau::uuid_t has been given, notification or indication will be disabled.
250 */
251 HCIStatusCode close() noexcept;
252
253 ~BTGattCmd() noexcept { close(); }
254
255 void setResponseMinSize(jau::nsize_t v) noexcept { rspMinSize = v; }
256 void setDataCallback(const DataCallback& dcb) noexcept { dataCallback = dcb; }
257
258 /** Return name, representing the command */
259 const std::string& getName() const noexcept { return name; }
260
261 /** Return command's BTGattService jau::uuid_t, may be nullptr. */
262 const jau::uuid_t* getServiceUUID() const noexcept { return service_uuid; }
263
264 /** Return command's BTGattChar value jau::uuid_t to write command, never nullptr. */
265 const jau::uuid_t* getCommandUUID() const noexcept { return cmd_uuid; }
266
267 /** Return true if a notification or indication response has been set via constructor, otherwise false. */
268 bool hasResponseSet() const noexcept { return nullptr != rsp_uuid; }
269
270 /** Return command's optional BTGattChar value jau::uuid_t for the notification or indication response, may be nullptr. */
271 const jau::uuid_t* getResponseUUID() const noexcept { return rsp_uuid; }
272
273 /** Set verbosity for UUID resolution. */
274 void setVerbose(const bool v) noexcept { verbose = v; }
275
276 /**
277 * Returns the read-only response data object
278 * for configured commands with response notification or indication.
279 *
280 * jau::TROOctets::size() matches the size of last received command response or zero.
281 * @see send()
282 */
283 const jau::TROOctets& getResponse() const noexcept { return rsp_data; }
284
285 /**
286 * Query whether all UUIDs of this commands have been resolved.
287 *
288 * In case no command has been issued via send() yet,
289 * the UUIDs will be resolved with this call.
290 *
291 * @return true if all UUIDs have been resolved, otherwise false
292 */
293 bool isResolved() noexcept;
294
295 /**
296 * Send the command to the remote BTDevice.
297 *
298 * If a notification or indication result jau::uuid_t has been set via constructor,
299 * it will be awaited and can be retrieved via getResponse() after command returns.
300 *
301 * @param prefNoAck pass true to prefer command write without acknowledge, otherwise use with-ack if available
302 * @param cmd_data raw command octets
303 * @param timeout maximum duration in fractions of seconds to wait for the response to become available, if any.
304 * @return
305 * @see getResponse()
306 */
307 HCIStatusCode send(const bool prefNoAck, const jau::TROOctets& cmd_data, const jau::fraction_i64& timeout) noexcept;
308
309 /**
310 * Send the command to the remote BTDevice, only.
311 *
312 * Regardless whether a notification or indication result jau::uuid_t has been set via constructor,
313 * this command will not wait for the response.
314 *
315 * @param prefNoAck pass true to prefer command write without acknowledge, otherwise use with-ack if available
316 * @param cmd_data raw command octets
317 * @return
318 * @see getResponse()
319 */
320 HCIStatusCode sendOnly(const bool prefNoAck, const jau::TROOctets& cmd_data) noexcept;
321
322 std::string toString() const noexcept;
323 };
324
325 /**@}*/
326
327}
328
329#endif // BT_GATT_CMD_HPP_
BTDevice represents one remote Bluetooth device.
Definition: BTDevice.hpp:81
BTGattChar event listener for notification and indication events.
Definition: BTGattChar.hpp:450
Class maps a GATT command and optionally its asynchronous response to a synchronous atomic operation.
Definition: BTGattCmd.hpp:67
void setVerbose(const bool v) noexcept
Set verbosity for UUID resolution.
Definition: BTGattCmd.hpp:274
const std::string & getName() const noexcept
Return name, representing the command.
Definition: BTGattCmd.hpp:259
BTGattCmd(BTDevice &dev_, std::string name_, const jau::uuid_t &cmd_uuid_, const jau::uuid_t &rsp_uuid_, const jau::nsize_t rsp_capacity) noexcept
Constructor for commands with notification or indication response.
Definition: BTGattCmd.hpp:175
bool hasResponseSet() const noexcept
Return true if a notification or indication response has been set via constructor,...
Definition: BTGattCmd.hpp:268
jau::function< void(BTGattCharRef charDecl, const jau::TROOctets &char_value, const uint64_t timestamp)> DataCallback
Definition: BTGattCmd.hpp:69
const jau::uuid_t * getServiceUUID() const noexcept
Return command's BTGattService jau::uuid_t, may be nullptr.
Definition: BTGattCmd.hpp:262
HCIStatusCode close() noexcept
Close this command instance, usually called at destruction.
Definition: BTGattCmd.cpp:133
HCIStatusCode send(const bool prefNoAck, const jau::TROOctets &cmd_data, const jau::fraction_i64 &timeout) noexcept
Send the command to the remote BTDevice.
Definition: BTGattCmd.cpp:176
BTGattCmd(BTDevice &dev_, std::string name_, const jau::uuid_t &cmd_uuid_) noexcept
Constructor for commands without response.
Definition: BTGattCmd.hpp:229
HCIStatusCode sendOnly(const bool prefNoAck, const jau::TROOctets &cmd_data) noexcept
Send the command to the remote BTDevice, only.
Definition: BTGattCmd.cpp:179
BTGattCmd(BTDevice &dev_, std::string name_, const jau::uuid_t &service_uuid_, const jau::uuid_t &cmd_uuid_) noexcept
Constructor for commands without response.
Definition: BTGattCmd.hpp:202
const jau::uuid_t * getCommandUUID() const noexcept
Return command's BTGattChar value jau::uuid_t to write command, never nullptr.
Definition: BTGattCmd.hpp:265
const jau::uuid_t * getResponseUUID() const noexcept
Return command's optional BTGattChar value jau::uuid_t for the notification or indication response,...
Definition: BTGattCmd.hpp:271
bool isResolved() noexcept
Query whether all UUIDs of this commands have been resolved.
Definition: BTGattCmd.cpp:167
const jau::TROOctets & getResponse() const noexcept
Returns the read-only response data object for configured commands with response notification or indi...
Definition: BTGattCmd.hpp:283
void setResponseMinSize(jau::nsize_t v) noexcept
Definition: BTGattCmd.hpp:255
void setDataCallback(const DataCallback &dcb) noexcept
Definition: BTGattCmd.hpp:256
std::string toString() const noexcept
Definition: BTGattCmd.cpp:262
Persistent endian aware octet data, i.e.
Definition: octets.hpp:560
Transient read only and endian aware octet data, i.e.
Definition: octets.hpp:67
static environment & get(const std::string &root_prefix_domain="jau") noexcept
Static singleton initialization of this project's environment with the given global root prefix_domai...
Class template jau::function is a general-purpose static-polymorphic function wrapper.
virtual std::string toString() const noexcept=0
Returns the string representation in BE network order, i.e.
lb_endian_t
Simplified reduced endian type only covering little- and big-endian.
Definition: byte_util.hpp:227
@ little
Identifier for little endian, equivalent to endian::little.
Entry * get(const EUI48 &addr, const std::string &name, AddressNameEntryMatchFunc m) noexcept
Returns a matching BTSecurityRegistry::Entry with the given addr and/or name.
HCIStatusCode
BT Core Spec v5.2: Vol 1, Part F Controller Error Codes: 1.3 List of Error Codes.
Definition: HCITypes.hpp:138
std::shared_ptr< BTGattChar > BTGattCharRef
Definition: BTGattChar.hpp:410
fraction< int64_t > fraction_i64
fraction using int64_t as integral type
uint_fast32_t nsize_t
Natural 'size_t' alternative using uint_fast32_t as its natural sized type.
Definition: int_types.hpp:53
__pack(...): Produces MSVC, clang and gcc compatible lead-in and -out macros.
Definition: backtrace.hpp:32
STL namespace.