Mistake on this page?
Report an issue in GitHub or email us
ATHandler.h
1 /*
2  * Copyright (c) 2017, Arm Limited and affiliates.
3  * SPDX-License-Identifier: Apache-2.0
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 #ifndef AT_HANDLER_H_
19 #define AT_HANDLER_H_
20 
21 #include "platform/mbed_retarget.h"
22 
23 #include "events/EventQueue.h"
24 #include "PlatformMutex.h"
25 #include "nsapi_types.h"
26 
27 #include "Callback.h"
28 
29 #include <cstdarg>
30 
31 namespace mbed {
32 
33 class FileHandle;
34 
35 /**
36  * If application calls associated FileHandle only from single thread context
37  * then locking between AT command and response is not needed. However,
38  * note that many cellular functions are called indirectly, for example with the socket API.
39  * If you are unsure, then AT_HANDLER_MUTEX must be defined.
40  */
41 #define AT_HANDLER_MUTEX
42 
43 extern const char *OK;
44 extern const char *CRLF;
45 
46 #define BUFF_SIZE 32
47 
48 /* AT Error types enumeration */
49 enum DeviceErrorType {
50  DeviceErrorTypeNoError = 0,
51  DeviceErrorTypeError, // AT ERROR
52  DeviceErrorTypeErrorCMS, // AT ERROR CMS
53  DeviceErrorTypeErrorCME // AT ERROR CME
54 };
55 
56 /** AT response error with error code and type */
57 struct device_err_t {
58  DeviceErrorType errType;
59  int errCode;
60 };
61 
62 /// Class for sending AT commands and parsing AT responses.
63 class ATHandler {
64 
65 public:
66  /** Constructor
67  *
68  * @param fh file handle used for reading AT responses and writing AT commands
69  * @param queue Event queue used to transfer sigio events to this thread
70  * @param timeout Timeout when reading for AT response
71  * @param output_delimiter delimiter used when parsing at responses, "\r" should be used as output_delimiter
72  * @param send_delay the minimum delay in ms between the end of last response and the beginning of a new command
73  */
74  ATHandler(FileHandle *fh, events::EventQueue &queue, uint32_t timeout, const char *output_delimiter, uint16_t send_delay = 0);
75  virtual ~ATHandler();
76 
77  /** Return used file handle.
78  *
79  * @return used file handle
80  */
81  FileHandle *get_file_handle();
82 
83  /** Get a new ATHandler instance, and update the linked list. Once the use of the ATHandler
84  * has finished, call to close() has to be made
85  *
86  * @param fileHandle filehandle used for reading AT responses and writing AT commands.
87  * If there is already an ATHandler with the same fileHandle pointer,
88  * then a pointer to that ATHandler instance will be returned with
89  * that ATHandler's queue, timeout, delimiter, send_delay and debug_on
90  * values
91  * @param queue Event queue used to transfer sigio events to this thread
92  * @param timeout Timeout when reading for AT response
93  * @param delimiter delimiter used when parsing at responses, "\r" should be used as output_delimiter
94  * @param send_delay the minimum delay in ms between the end of last response and the beginning of a new command
95  * @param debug_on Set true to enable debug traces
96  * @return NULL, if fileHandle is not set, or a pointer to an existing ATHandler, if the fileHandle is
97  * already in use. Otherwise a pointer to a new ATHandler instance is returned
98  */
99  static ATHandler *get_instance(FileHandle *fileHandle, events::EventQueue &queue, uint32_t timeout,
100  const char *delimiter, uint16_t send_delay, bool debug_on);
101 
102  /** Close and delete the current ATHandler instance, if the reference count to it is 0.
103  * Close() can be only called, if the ATHandler was opened with get_instance()
104  *
105  * @return NSAPI_ERROR_OK on success, NSAPI_ERROR_PARAMETER on failure
106  */
107  nsapi_error_t close();
108 
109  /** Locks the mutex for file handle if AT_HANDLER_MUTEX is defined.
110  */
111  void lock();
112 
113  /** Unlocks the mutex for file handle if AT_HANDLER_MUTEX is defined.
114  */
115  void unlock();
116 
117  /** Locks the mutex for file handle if AT_HANDLER_MUTEX is defined and returns the last error.
118  *
119  * @return last error that happened when parsing AT responses
120  */
121  nsapi_error_t unlock_return_error();
122 
123  /** Set callback function for URC
124  *
125  * @param prefix URC text to look for, e.g. "+CMTI:". Maximum length is BUFF_SIZE.
126  * @param callback function to call on prefix, or 0 to remove callback
127  */
128  void set_urc_handler(const char *prefix, Callback<void()> callback);
129 
130  ATHandler *_nextATHandler; // linked list
131 
132  /** returns the last error while parsing AT responses.
133  *
134  * @return last error
135  */
136  nsapi_error_t get_last_error() const;
137 
138  /** returns the last device error while parsing AT responses. Actually AT error (CME/CMS).
139  *
140  * @return last error struct device_err_t
141  */
142  device_err_t get_last_device_error() const;
143 
144  /** Increase reference count. Used for counting references to this instance.
145  * Note that this should be used with care, if the ATHandler was taken into use
146  * with get_instance()
147  */
148  void inc_ref_count();
149 
150  /** Decrease reference count. Used for counting references to this instance.
151  * Note that this should be used with care, if the ATHandler was taken into use
152  * with get_instance()
153  */
154  void dec_ref_count();
155 
156  /** Get the current reference count. Used for counting references to this instance.
157  *
158  * @return current reference count
159  */
160  int get_ref_count();
161 
162  /** Set timeout in milliseconds for AT commands
163  *
164  * @param timeout_milliseconds Timeout in milliseconds
165  * @param default_timeout Store as default timeout
166  */
167  void set_at_timeout(uint32_t timeout_milliseconds, bool default_timeout = false);
168 
169  /** Set timeout in milliseconds for all ATHandlers in the _atHandlers list
170  *
171  * @param timeout_milliseconds Timeout in milliseconds
172  * @param default_timeout Store as default timeout
173  */
174  static void set_at_timeout_list(uint32_t timeout_milliseconds, bool default_timeout = false);
175 
176  /** Restore timeout to previous timeout. Handy if there is a need to change timeout temporarily.
177  */
178  void restore_at_timeout();
179 
180  /** Clear pending error flag. By default, error is cleared only in lock().
181  */
182  void clear_error();
183 
184  /**
185  * Flushes the underlying stream
186  */
187  void flush();
188 
189  /** Tries to find oob's from the AT response. Call the urc callback if one is found.
190  */
191  void process_oob();
192 
193  /** Set file handle, which is used for reading AT responses and writing AT commands
194  *
195  * @param fh file handle used for reading AT responses and writing AT commands
196  */
197  void set_file_handle(FileHandle *fh);
198 
199  /** Set is file handle usable. Some situations like after going to data mode, file handle is not usable anymore.
200  * Any items in queue are not to be processed.
201  *
202  * @param usable true for usable filehandle
203  */
204  void set_is_filehandle_usable(bool usable);
205 
206  /** Synchronize AT command and response handling to modem.
207  *
208  * @param timeout_ms ATHandler timeout when trying to sync. Will be restored when function returns.
209  * @return true is synchronization was successful, false in case of failure
210  */
211  bool sync(int timeout_ms);
212 
213 protected:
214  void event();
215 #ifdef AT_HANDLER_MUTEX
216  PlatformMutex _fileHandleMutex;
217 #endif
218  FileHandle *_fileHandle;
219 private:
220  /** Remove urc handler from linked list of urc's
221  *
222  * @param prefix Register urc prefix for callback. Urc could be for example "+CMTI: "
223  */
224  void remove_urc_handler(const char *prefix);
225 
226  void set_error(nsapi_error_t err);
227 
228  events::EventQueue &_queue;
229  nsapi_error_t _last_err;
230  int _last_3gpp_error;
231  device_err_t _last_at_err;
232  uint16_t _oob_string_max_length;
233  char *_output_delimiter;
234 
235  struct oob_t {
236  const char *prefix;
237  int prefix_len;
238  Callback<void()> cb;
239  oob_t *next;
240  };
241  oob_t *_oobs;
242  uint32_t _at_timeout;
243  uint32_t _previous_at_timeout;
244 
245  uint16_t _at_send_delay;
246  uint64_t _last_response_stop;
247 
248  bool _oob_queued;
249  int32_t _ref_count;
250  bool _is_fh_usable;
251 
252  static ATHandler *_atHandlers;
253 
254  //*************************************
255 public:
256 
257  /** Starts the command writing by clearing the last error and writing the given command.
258  * In case of failure when writing, the last error is set to NSAPI_ERROR_DEVICE_ERROR.
259  *
260  * @param cmd AT command to be written to modem
261  */
262  virtual void cmd_start(const char *cmd);
263 
264  /**
265  * @brief cmd_start_stop Starts an AT command, writes given variadic arguments and stops the command. Use this
266  * command when you need multiple response parameters to be handled.
267  * NOTE: Does not lock ATHandler for process!
268  *
269  * @param cmd AT command in form +<CMD> (will be used also in response reading, no extra chars allowed)
270  * @param cmd_chr Char to be added to specific AT command: '?', '=' or ''. Will be used as such so '=1' is valid as well.
271  * @param format Format string for variadic arguments to be added to AT command; No separator needed.
272  * Use %d for integer, %s for string and %b for byte string (requires 2 arguments: string and length)
273  */
274  void cmd_start_stop(const char *cmd, const char *cmd_chr, const char *format = "", ...);
275 
276  /**
277  * @brief at_cmd_str Send an AT command and read a single string response. Locks and unlocks ATHandler for operation
278  * @param cmd AT command in form +<CMD> (will be used also in response reading, no extra chars allowed)
279  * @param cmd_chr Char to be added to specific AT command: '?', '=' or ''. Will be used as such so '=1' is valid as well.
280  * @param resp_buf Response buffer
281  * @param resp_buf_size Response buffer size
282  * @param format Format string for variadic arguments to be added to AT command; No separator needed.
283  * Use %d for integer, %s for string and %b for byte string (requires 2 arguments: string and length)
284  * @return last error that happened when parsing AT responses
285  */
286  nsapi_error_t at_cmd_str(const char *cmd, const char *cmd_chr, char *resp_buf, size_t resp_buf_size, const char *format = "", ...);
287 
288  /**
289  * @brief at_cmd_int Send an AT command and read a single integer response. Locks and unlocks ATHandler for operation
290  * @param cmd AT command in form +<CMD> (will be used also in response reading, no extra chars allowed)
291  * @param cmd_chr Char to be added to specific AT command: '?', '=' or ''. Will be used as such so '=1' is valid as well.
292  * @param resp Integer to hold response
293  * @param format Format string for variadic arguments to be added to AT command; No separator needed.
294  * Use %d for integer, %s for string and %b for byte string (requires 2 arguments: string and length)
295  * @return last error that happened when parsing AT responses
296  */
297  nsapi_error_t at_cmd_int(const char *cmd, const char *cmd_chr, int &resp, const char *format = "", ...);
298 
299  /**
300  * @brief at_cmd_discard Send an AT command and read and discard a response. Locks and unlocks ATHandler for operation
301  * @param cmd AT command in form +<CMD> (will be used also in response reading, no extra chars allowed)
302  * @param cmd_chr Char to be added to specific AT command: '?', '=' or ''. Will be used as such so '=1' is valid as well.
303  * @param format Format string for variadic arguments to be added to AT command; No separator needed.
304  * Use %d for integer, %s for string and %b for byte string (requires 2 arguments: string and length)
305  * @return last error that happened when parsing AT responses
306  */
307  nsapi_error_t at_cmd_discard(const char *cmd, const char *cmd_chr, const char *format = "", ...);
308 
309 public:
310 
311  /** Writes integer type AT command subparameter. Starts with the delimiter if not the first param after cmd_start.
312  * In case of failure when writing, the last error is set to NSAPI_ERROR_DEVICE_ERROR.
313  *
314  * @param param int to be written to modem as AT command subparameter
315  */
316  void write_int(int32_t param);
317 
318  /** Writes string type AT command subparamater. Quotes are added to surround the given string.
319  * Starts with the delimiter if not the first param after cmd_start.
320  * In case of failure when writing, the last error is set to NSAPI_ERROR_DEVICE_ERROR.
321  *
322  * @param param string to be written to modem as AT command subparameter
323  * @param useQuotations flag indicating whether the string should be included in quotation marks
324  */
325  void write_string(const char *param, bool useQuotations = true);
326 
327  /** Stops the AT command by writing command-line terminator CR to mark command as finished.
328  */
329  void cmd_stop();
330 
331  /** Stops the AT command by writing command-line terminator CR to mark command as finished and reads the OK/ERROR response.
332  *
333  */
334  void cmd_stop_read_resp();
335 
336  /** Write bytes without any subparameter delimiters, such as comma.
337  * In case of failure when writing, the last error is set to NSAPI_ERROR_DEVICE_ERROR.
338  *
339  * @param data bytes to be written to modem
340  * @param len length of data string
341  *
342  * @return number of characters successfully written
343  */
344  size_t write_bytes(const uint8_t *data, size_t len);
345 
346  /** Sets the stop tag for the current scope (response/information response/element)
347  * Parameter's reading routines will stop the reading when such tag is found and will set the found flag.
348  * Consume routines will read everything until such tag is found.
349  *
350  * @param stop_tag_seq string to be set as stop tag
351  */
352  void set_stop_tag(const char *stop_tag_seq);
353 
354  /** Sets the delimiter between parameters or between elements of the information response.
355  * Parameter's reading routines will stop when such char is read.
356  *
357  * @param delimiter char to be set as _delimiter
358  */
359  void set_delimiter(char delimiter);
360 
361  /** Sets the delimiter to default value defined by DEFAULT_DELIMITER.
362  */
363  void set_default_delimiter();
364 
365  /** Defines behaviour for using or ignoring the delimiter within an AT command
366  *
367  * @param use_delimiter indicating if delimiter should be used or not
368  */
369  void use_delimiter(bool use_delimiter);
370 
371  /** Consumes the reading buffer up to the delimiter or stop_tag
372  *
373  * @param count number of parameters to be skipped
374  */
375  void skip_param(uint32_t count = 1);
376 
377  /** Consumes the given length from the reading buffer
378  *
379  * @param len length to be consumed from reading buffer
380  * @param count number of parameters to be skipped
381  */
382  void skip_param(ssize_t len, uint32_t count);
383 
384  /** Reads given number of bytes from receiving buffer without checking any subparameter delimiters, such as comma.
385  *
386  * @param buf output buffer for the read
387  * @param len maximum number of bytes to read
388  * @return number of successfully read bytes or -1 in case of error
389  */
390  ssize_t read_bytes(uint8_t *buf, size_t len);
391 
392  /** Reads chars from reading buffer. Terminates with null. Skips the quotation marks.
393  * Stops on delimiter or stop tag.
394  *
395  * @param str output buffer for the read
396  * @param size maximum number of chars to output including NULL
397  * @param read_even_stop_tag if true then try to read even if the stop tag was found previously
398  * @return length of output string or -1 in case of read timeout before delimiter or stop tag is found
399  */
400  ssize_t read_string(char *str, size_t size, bool read_even_stop_tag = false);
401 
402  /** Reads chars representing hex ascii values and converts them to the corresponding chars.
403  * For example: "4156" to "AV".
404  * Terminates with null. Skips the quotation marks.
405  * Stops on delimiter or stop tag.
406  *
407  * @param str output buffer for the read
408  * @param size maximum number of chars to output
409  * @return length of output string or -1 in case of read timeout before delimiter or stop tag is found
410  */
411  ssize_t read_hex_string(char *str, size_t size);
412 
413  /** Reads as string and converts result to integer. Supports only positive integers.
414  *
415  * @return the positive integer or -1 in case of error.
416  */
417  int32_t read_int();
418 
419  /** This looks for necessary matches: prefix, OK, ERROR, URCs and sets the correct scope.
420  *
421  * @param prefix string to be matched from receiving buffer. If not NULL and match succeeds, then scope
422  * will be set as information response(info_type)
423  * @param stop flag to indicate if we go to information response scope or not.
424  * (needed when nothing is expected to be received anymore after the prefix match:
425  * sms case: "> ", bc95 reboot case)
426  */
427  void resp_start(const char *prefix = NULL, bool stop = false);
428 
429  /** Ends all scopes starting from current scope.
430  * Consumes everything until the scope's stop tag is found, then
431  * goes to next scope until response scope is ending.
432  * URC match is checked during response scope ending,
433  * for every new line / CRLF.
434  *
435  *
436  * Possible sequence:
437  * element scope -> information response scope -> response scope
438  */
439  void resp_stop();
440 
441  /** Looks for matching the prefix given to resp_start() call.
442  * If needed, it ends the scope of a previous information response.
443  * Sets the information response scope if new prefix is found and response scope if prefix is not found.
444  *
445  * @return true if prefix defined for information response is not empty string and is found,
446  * false otherwise.
447  */
448  bool info_resp();
449 
450  /** Looks for matching the start tag.
451  * If needed, it ends the scope of a previous element.
452  * Sets the element scope if start tag is found and information response scope if start tag is not found.
453  *
454  * @param start_tag tag to be matched to begin parsing an element of an information response
455  * @return true if new element is found, false otherwise
456  */
457  bool info_elem(char start_tag);
458 
459  /** Consumes the received content until current stop tag is found.
460  *
461  * @return true if stop tag is found, false otherwise
462  */
463  bool consume_to_stop_tag();
464 
465  /** Return the last 3GPP error code.
466  * @return last 3GPP error code
467  */
468  int get_3gpp_error();
469 
470 public: // just for debugging
471  /**
472  * AT debugging, when enabled will print all data read and written,
473  * non-printable chars are printed as "[%d]".
474  *
475  * AT debug can be enabled at compile time using MBED_CONF_CELLULAR_DEBUG_AT flag or at runtime
476  * calling set_debug(). Note that MBED_CONF_MBED_TRACE_ENABLE must also be enabled.
477  *
478  * @param debug_on Enable/disable debugging
479  */
480  void set_debug(bool debug_on);
481 
482  /**
483  * Get degug state set by @ref set_debug
484  *
485  * @return current state of debug
486  */
487  bool get_debug() const;
488 
489  /** Set debug_on for all ATHandlers in the _atHandlers list
490  *
491  * @param debug_on Set true to enable debug traces
492  */
493  static void set_debug_list(bool debug_on);
494 
495 private:
496 
497  // should fit any prefix and int
498  char _recv_buff[BUFF_SIZE];
499  // reading position
500  size_t _recv_len;
501  // reading length
502  size_t _recv_pos;
503 
504  // resp_type: the part of the response that doesn't include the information response (+CMD1,+CMD2..)
505  // ends with OK or (CME)(CMS)ERROR
506  // info_type: the information response part of the response: starts with +CMD1 and ends with CRLF
507  // information response contains parameters or subsets of parameters (elements), both separated by comma
508  // elem_type: subsets of parameters that are part of information response, its parameters are separated by comma
509  enum ScopeType {RespType, InfoType, ElemType, NotSet};
510  void set_scope(ScopeType scope_type);
511  ScopeType _current_scope;
512 
513  struct tag_t {
514  char tag[7];
515  size_t len;
516  bool found;
517  };
518 
519  // tag to stop response scope
520  tag_t _resp_stop;
521  // tag to stop information response scope
522  tag_t _info_stop;
523  // tag to stop element scope
524  tag_t _elem_stop;
525  // reference to the stop tag of current scope (resp/info/elem)
526  tag_t *_stop_tag;
527 
528  // delimiter between parameters and also used for delimiting elements of information response
529  char _delimiter;
530  // set true on prefix match -> indicates start of an information response or of an element
531  bool _prefix_matched;
532  // set true on urc match
533  bool _urc_matched;
534  // set true on (CME)(CMS)ERROR
535  bool _error_found;
536  // Max length of OK,(CME)(CMS)ERROR and URCs
537  size_t _max_resp_length;
538 
539  // prefix set during resp_start and used to try matching possible information responses
540  char _info_resp_prefix[BUFF_SIZE];
541  bool _debug_on;
542  bool _cmd_start;
543  bool _use_delimiter;
544 
545  // time when a command or an URC processing was started
546  uint64_t _start_time;
547 
548  char _cmd_buffer[BUFF_SIZE];
549 
550 private:
551  //Handles the arguments from given variadic list
552  void handle_args(const char *format, std::va_list list);
553 
554  //Starts an AT command based on given parameters
555  void handle_start(const char *cmd, const char *cmd_chr);
556 
557  //Checks that ATHandler does not have a pending error condition and filehandle is usable
558  bool ok_to_proceed();
559 
560 private:
561  // Gets char from receiving buffer.
562  // Resets and fills the buffer if all are already read (receiving position equals receiving length).
563  // Returns a next char or -1 on failure (also sets error flag)
564  int get_char();
565  // Sets to 0 the reading position, reading length and the whole buffer content.
566  void reset_buffer();
567  // Reading position set to 0 and buffer's unread content moved to beginning
568  void rewind_buffer();
569  // Calculate remaining time for polling based on request start time and AT timeout.
570  // Returns 0 or time in ms for polling.
571  int poll_timeout(bool wait_for_timeout = true);
572  // Reads from serial to receiving buffer.
573  // Returns true on successful read OR false on timeout.
574  bool fill_buffer(bool wait_for_timeout = true);
575 
576  void set_tag(tag_t *tag_dest, const char *tag_seq);
577 
578  // Rewinds the receiving buffer and compares it against given str.
579  bool match(const char *str, size_t size);
580  // Iterates URCs and checks if they match the receiving buffer content.
581  // If URC match sets the scope to information response and after urc's cb returns
582  // finishes the information response scope(consumes to CRLF).
583  bool match_urc();
584  // Checks if any of the error strings are matching the receiving buffer content.
585  bool match_error();
586  // Checks if current char in buffer matches ch and consumes it,
587  // if no match lets the buffer unchanged.
588  bool consume_char(char ch);
589  // Consumes the received content until tag is found.
590  // Consumes the tag only if consume_tag flag is true.
591  bool consume_to_tag(const char *tag, bool consume_tag);
592  // Checks if receiving buffer contains OK, ERROR, URC or given prefix.
593  void resp(const char *prefix, bool check_urc);
594 
595 
596  ScopeType get_scope();
597 
598  // Consumes to information response stop tag which is CRLF. Sets scope to response.
599  void information_response_stop();
600  // Consumes to element stop tag. Sets scope to information response
601  void information_response_element_stop();
602 
603  // Reads the error code if expected and sets it as last error.
604  void at_error(bool error_code, DeviceErrorType error_type);
605 
606  /** Convert AT error code to 3GPP error codes
607  * @param err AT error code read from CME/CMS ERROR responses
608  * @param error_type error type (CMS/CME/ERROR)
609  */
610  void set_3gpp_error(int err, DeviceErrorType error_type);
611 
612  bool check_cmd_send();
613  size_t write(const void *data, size_t len);
614 
615  /** Finds occurrence of one char buffer inside another char buffer.
616  *
617  * @param dest destination char buffer
618  * @param dest_len length of dest
619  * @param src string to be searched for
620  * @param src_len length of string to be searched for
621  *
622  * @return pointer to first occurrence of src in dest
623  */
624  const char *mem_str(const char *dest, size_t dest_len, const char *src, size_t src_len);
625 
626  // check is urc is already added
627  bool find_urc_handler(const char *prefix);
628 
629  // print contents of a buffer to trace log
630  enum ATType {
631  AT_ERR,
632  AT_RX,
633  AT_TX
634  };
635  void debug_print(const char *p, int len, ATType type);
636 };
637 
638 } // namespace mbed
639 
640 #endif //AT_HANDLER_H_
EventQueue.
Definition: EventQueue.h:52
signed int nsapi_error_t
Type used to represent error codes.
Definition: nsapi_types.h:95
Class FileHandle.
Definition: FileHandle.h:46
Callback< R()> callback(R(*func)()=0)
Create a callback class with type inferred from the arguments.
Definition: Callback.h:3848
The PlatformMutex class is used to synchronize the execution of threads.
Definition: PlatformMutex.h:47
AT response error with error code and type.
Definition: ATHandler.h:57
Callback class based on template specialization.
Definition: Callback.h:39
Class for sending AT commands and parsing AT responses.
Definition: ATHandler.h:63
Important Information for this Arm website

This site uses cookies to store information on your computer. By continuing to use our site, you consent to our cookies. If you are not happy with the use of these cookies, please review our Cookie Policy to learn how they can be disabled. By disabling cookies, some features of the site will not work.